/// @PACKAGEDEFAULT MissionFramework
#include "constants.h"
#include "ACV_constantes.h"
#include "mission_setup.sqf"

// Everytime an object is added to the mission, it's stored in here. It will allow for clean up
d_mission_object_list = [];
// Everytime a marker is added to the mission, it's stored in here. It will allow for clean up
d_mission_marker_list = [];
// Everytime an unit is added to the mission, it's stored in here. It will allow for clean up
d_mission_unit_list = [];
// Everytime a trigger is added to the mission, it's stored in here. It will allow for clean up
d_mission_trigger_list = [];
// Everytime an action is added to the player for the mission, it's stored in here. It will allow for clean up
d_mission_actions_list = [];
// When the player picks up an item, it's added in here in the form: [object_class, action, [action parameters], object_desc]
d_mission_picked_up_items = [];


// Used in the mission selection dialog
d_mission_dlg_weather = [["Random",-1,-1],["Clear",0,0.2],["Cloudy",0,0.8],["Light rain",0.3,0.7],["Medium rain",0.5,0.7],["Stormy",0.8,1]];
d_mission_dlg_power = [["Random",-1,-1],["Automatic",0,0],["Improved",1,0],["Leveled Up",2,0],["Heavy",3,1],["Nightmare",4,3]];
d_mission_dlg_airsupport = [["Random",-1],["None",0],["Few",0.3],["Medium",0.6],["Normal",1],["Increased",1.6],["Heavy",2],["Unbearable",4]];
d_mission_dlg_reinforcement = [["Random",-1],["None",0],["Rarely",1],["Regularly",2],["Frequently",5],["You're toast!",7]];
d_mission_dlg_communication = [["Random",-1],["100m",100],["200m",200],["300m",300],["400m",400],["500m",500],["600m",600],["700m",700],["800m",800],["900m",900]];
d_mission_dlg_snipers = [["Random",-1,-1],["None",0,0],["Rare",0,1],["Few",1,1],["Some",1,3],["A lot",2,4],["Everywhere!",5,8]];

// Global Variables of the mission files
DM_Mission_Name="";
DM_Mission_Desc="";
DM_Mission_Keywords="";
DM_Mission_Chapters = 1;
DM_Mission_FolderName = "";
DM_Mission_StartTime = [7,0]; // In the format [hours, minutes];
DM_Mission_StartRain = 0; // 0 = no rain, 1 = max rain, -1 = random
DM_Mission_StartOvercast = 0; // 0 = no clouds,  1 = stormy, -1 = random
DM_Mission_Custom_Groups = []; // Contains a list of custom made groups used by the mission in the form:
							   // ["GroupName","SIDE",["unittype1","unittype2",...]]; -- not that the first unit is always the leader.
							   // ex: ["Spetznaz","EAST",["RUS_Soldier_TL","RUS_Soldier1","RUS_Soldier_GL"]];

// Variables internal to the mission:
DM_Mission_Language = "EN"; // Not used yet but ready for later :)
DM_Mission_EnemyPower = 0; // 0: Default, Min=1 , Max=5+
DM_Mission_EnemyCoeff = 0; // 0: Default, Min=1 , Max=5+
DM_Mission_Chapter_Success = 0; // 0 is pending, 1 = success, -1 = failure. You can use other values in chapters for multi-branching.
DM_Mission_Mission_CanRun = True; // Setting it to false will exit the chapters and mission execution. This allows for interrupting the mission.
DM_Mission_Mission_Runs = False; // This will be set to false when the mission is stopped
DM_Mission_Chapter_CanRun = True; // Setting it to false will exit the chapters and mission execution. This allows for interrupting the mission.
DM_Mission_Chapter_Runs = False; // The current status of the running chapter
DM_Mission_RandomReinforcement = 0; // When a mission is running, this will send reinforcement from time to time if the mission current position is defined.
DM_Mission_Snipers = 0; // When creating a chapter, this will create snipers on the mission position randomly.
DM_Mission_AirSupport = 0; // This will create aerial vehicles on mission spots if used in the mission script.

// Will randomly turn one civilian into Kamikaze 
if (isnil "DM_Mission_KamikazeCivilian") then { DM_Mission_KamikazeCivilian = false; };

// We save the initial state of the UPSMON sharedist. So we can set it back after a mission.
DM_Mission_UPSMON_Share_Dist = 300;
If (!isNil "KRON_UPS_sharedist") then {DM_Mission_UPSMON_Share_Dist = KRON_UPS_sharedist};

DM_MISSION_STORE = ObjNull;
If (isServer) then {
	DM_MISSION_STORE = createVehicle ["HeliHEmpty", [0,0,0], [], 0, "NONE"];
};

// ===========================================================================
//							OBJECT TYPES AND PARAMS
// ===========================================================================
// 
// REGISTER
//		This is for all units/objects which are part of the mission and which don't have a special function. We just want to register them.
//		This will allow for cleaning them up automagically.
//		You only need to set the Action on those and not the rest.
//
// DESTROY
//		The object needs to be destroyed by the player in order for the objective to be completed.
//		- The object
//      - The array of arguments:
//	 		- The message displayed to everyone when the object is destroyed
//			- The name of a JIPH variable shared between everyone.
//			- (Optional) The value this variable will receive when the object is destroyed. Default = true
// 			- (Optional) The number of satchels needed to destroy the item. Default = 2
//			- (Optional) An array of ammunitions which are taken into account for the damages as replacement for the satchel
//			- (Optional) A string containing code which will be executed on all systems when the object is destroyed.
//		Ex: ["Good job! The tower is destoyed","jip_tower_is_destroyed",True,1,["PipeBomb","GrenadeHand"],"hint 'The tower is destroyed';"]
//
// PICKUP
//		The object will be picked up by a player. A global variable is set to say the object is found
//		- The object
//		- The arguments:
//	 		- The object "description" like "Olaf's Laptop", this is used later to allow dropping the item. This is also used as a key to give the item to someone.
//	 		- The message displayed to everyone when the object is picked up
//			- The name of a JIPH variable shared between everyone.
//			- (Optional) The title of the action which appears when approaching the object
//			- (Optional) The value this variable will receive when the object is picked up. Default = true
//			- (Optional) A string containing code which will be executed when the object is picked up.
//		Ex: ["Laptop","You found the laptop","jip_laptop_found","Take the laptop",True,"hint 'The laptop has been found';"]
//
// ACTIVATE
//		The object can be activate/deactivated by the player which will trigger a message plus set a global variable
//		- The object
//		- The arguments:
//	 		- The message displayed to everyone when the object is activated
// 			- The message displayed to everyone when the object is deactivated
//			- The name of a JIPH variable shared between everyone. This variable is true when it's activated and false when it is not.
//			- (Optional) The action title when the item is activated
//			- (Optional) The action title when the item is deactivated
//			- (Optional) The initial status of the item. Either deactivated (False = default) or activated (True)
//			- (Optional) A string containing code which will be executed when the object is activated.
//			- (Optional) A string containing code which will be executed when the object is deactivated.
//		Ex: ["The generator is on","The generator is off","jip_generator_status","Turn Off","Turn On",false,"hint 'Generator on hint';","hint 'Generator off hint';"]
//
// ASSASSINATE
//		The person has to be killed
//		- The object
//		- The arguments:
//	 		- The message displayed to everyone when the person is killed
//			- The name of a JIPH variable shared between everyone. This variable is true when the person is dead and false the rest of the time.
//			- (Optional) A string containing code which will be executed when the person get killed.
//		Ex: ["You killed the traitor","jip_traitor_killed"]
//
// CONQUER
//		The area needs to be cleaned of all enemies. The enemy side is the one defined in the setup
//		- The object which is the center of the zone. Use invisible helipads, otherwise you cannot pass the action type and parameters to the system.
//		- The arguments:
//	 		- The message displayed to everyone when the zone is freed from enemies
//	 		- The area which needs to be freed in the form: [[centerpos],[size],angle,true]  where true means rectangle and false circle. 
//	 		- The side of the enemies present in the area
//			- The name of a JIPH variable shared between everyone. This variable is true when the zone is clear and false otherwise.
//	 		- The quantity of enemies left to consider it done
//			- (Optional) A string containing code which will be executed when the zone got freed.
//
// RESCUE
//		The person needs to be untied and optionally taken back to a given position
//		- The person which needs to be freed
//		- The arguments:
//	 		- The message displayed to everyone when the is untied
//	 		- The message displayed to everyone when the person dies
//			- The name of a JIPH variable shared between everyone. This variable is true when the person is not captive anymore and false otherwise.
//			- The name of a JIPH variable shared between everyone. This variable is true when the person is alive and false otherwise.
//			- (Optional) A string containing code which will be executed when the person is freed.
//			- (Optional) A string containing code which will be executed if the person dies.
//		Ex: ["You freed the civilian, congratulations!","You did your best...","jip_civ_free","jip_civ_alive","hint 'Civ is freed hint';","hint 'Civ is dead hint';"]
//
// LEAD
//		The person needs to be untied and optionally taken back to a given position
//		- The person which needs to be led
//		- The arguments:
//			- The message displayed to everyone when the person reaches the destination
//	 		- The message displayed to everyone when the person dies
//			- The name of a JIPH variable shared between everyone. This variable is true when the person reaches the destination
//			- The name of a JIPH variable shared between everyone. This variable is true when the person is alive and false otherwise.
//	 		- The area where the person needs to go in the form: [[centerpos],[size],angle,true]  where true means rectangle and false circle. Do not use if not needed
//			- (Optional) A string containing code which will be executed when the person reaches the destination
//			- (Optional) A string containing code which will be executed if the person dies.
//		Ex: ["You brought our soldier back, congratulations!","You did your best...","jip_soldier_arrived","jip_soldier_alive",[[centerpos],[size],angle,true],"hint 'Soldier arrived hint';","hint 'Soldier dead hint';"]
//
// ARREST
//		The person needs to be arrested, it is done by either killing all units around him or by wounding him
//		- The person which needs to be arrested
//		- The arguments:
//			- The message displayed to everyone when the person is arrested
//	 		- The message displayed to everyone when the person dies
//			- The name of a JIPH variable shared between everyone. This variable is true when the person is arrested
//			- The name of a JIPH variable shared between everyone. This variable is true when the person is alive and false otherwise.
//			- (Optional) A string containing code which will be executed when the person is arrested
//			- (Optional) A string containing code which will be executed if the person dies.
//		Ex: ["You caught Visnajev, congratulations!","Damn, you killed him....","jip_visnajev_arrested","jip_visnajev_alive","hint 'Visnajev is caught hint';","hint 'Visnajev is dead hint';"]
//
// TAKEOVER
//		The zone around the center of this object needs to be taken over by a side against another side
//		- The object around which the zone is defined.
//		- The arguments:
//			- The size of the zone to take over
//			- The message displayed to everyone when the zone is taken over
//	 		- The message displayed to everyone when the zone is lost
//			- The name of a JIPH variable shared between everyone. This variable is true when the zone is taken over, false otherwise
//			- (Optional) The time to hold the zone in seconds (default 20)
//			- (Optional) The Side which is the owner of the zone
//			- (Optional) The Side which needs to take over the zone
//			- (Optional) A string containing code which will be executed when the zone is taken over
//			- (Optional) A string containing code which will be executed when the zone is lost
//		Ex: [7,"The bunker has been taken over by the allies!","Damn, they took the bunker back!","jip_target_takenover",20,east,west,"hint 'Bunker is taken over';","hint 'Bunker is lost';"]
//

// ===========================================================================
//   					       COMMON FUNCTIONS
// ===========================================================================

// Returns True if the ACV is initialized. In such a case, we can use it for the mission functions
XfMissionACVEnabled = {
	private ["_return"];
	_return = !(isNil "ACV_Unit_Stock");
	_return;
};

XfMissionNextJipID = {
	private "_id";
	_id = ["DM_MISSION_JIP_NEXTID",0] call XNetGetJIP;
	if (_id == 0) then { _id = 1;};
	["DM_MISSION_JIP_NEXTID",(_id + 1)] call XNetSetJIP; 
	_id;
};

XfMissionGetInstance = {
	private ["_InstanceList","_InstanceValues","_instance","_id","_index","_value"];
	_id = _this select 0;
	if (typename _id == "SCALAR") then {
		_InstanceList = ["DM_MISSION_INSTANCES",[]] call XNetGetJIP;
		_index = _InstanceList find _id;
		if (_index >= 0) then {
			_InstanceValues = ["DM_MISSION_INSTANCE_VALUES",[]] call XNetGetJIP;
			If (_index < count (_InstanceValues)) then {
				_InstanceValues = _InstanceValues select _index;
				// Now we check if we got the values back of the right instance
				_value = [_InstanceValues, "__INSTANCE_ID__",-1] call XfHashGet;
				if (_value == _id) then {
					_instance = _InstanceValues;
				} else {
					diag_log Format["(%1)[%2]: %3: %4 %5",diag_tickTime,"mission_functions.sqf","XfMissionGetInstance","Found instance values do not match",[_id,_index,_value]];
				};
			} else {
				diag_log Format["(%1)[%2]: %3: %4 %5",diag_tickTime,"mission_functions.sqf","XfMissionGetInstance","Values index not found",[_id,_index]];
			};
		} else {
			diag_log Format["(%1)[%2]: %3: %4 %5",diag_tickTime,"mission_functions.sqf","XfMissionGetInstance","Instance not found",_this];
		};
	};
	_instance;
};

// When a mission object is added to the server, it is registered on the server
XfMissionRegisterObject = {
	private ["_object","_action","_parameters"];
	if (typename _this != "OBJECT") exitWith {diag_log Format["(%1)[%2]: %3 (%4)",diag_tickTime,"mission_functions.sqf - xfMissionRegisterObject","Argument is not an object",_this];};
	_object = _this;
	_action = _object call XfMissionGetAction;
	_parameters = _object call XfMissionGetParameters;
	if (isNil "_parameters") then {_parameters = []};
	If (_object in d_mission_object_list) then {
		diag_log Format["(%1)[%2]: %3 (%4)",diag_tickTime,"mission_functions.sqf - xfMissionRegisterObject","The object is already registered",_this];
	} else {
		d_mission_object_list set [count d_mission_object_list, _object];
	};
	// Executing the server side function
	_action = toUpper _action;
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 => %5",diag_tickTime,"mission_functions.sqf","Register","Object list",count(d_mission_object_list)];};
	If (X_Server) then {
		// Used to prevent executing multiple times a particular action event.
		_object setVariable ["mission_action_event_executed",false];
		Switch (_action) do {
			case "REGISTER":{
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 (%5) => %6",diag_tickTime,"mission_functions.sqf","S Register Only",_object,typeOf _object,_parameters];};
			};
			case "DESTROY":{
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 (%5) => %6",diag_tickTime,"mission_functions.sqf","S Register Destroy",_object,typeOf _object,_parameters];};
				[_object, _action, _parameters] call XfMissionObjectServerSetupDestroy;
			};
			case "PICKUP":{
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 (%5) => %6",diag_tickTime,"mission_functions.sqf","S Register Pickup",_object,typeOf _object,_parameters];};
				[_object, _action, _parameters] call XfMissionObjectServerSetupPickup;
			};
			case "ACTIVATE":{
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 (%5) => %6",diag_tickTime,"mission_functions.sqf","S Register Activate",_object,typeOf _object,_parameters];};
				[_object, _action, _parameters] call XfMissionObjectServerSetupActivate;
			};
			case "ASSASSINATE":{
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 (%5) => %6",diag_tickTime,"mission_functions.sqf","S Register Assassinate",_object,typeOf _object,_parameters];};
				[_object, _action, _parameters] call XfMissionObjectServerSetupAssassinate;
			};
			case "CONQUER":{
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 (%5) => %6",diag_tickTime,"mission_functions.sqf","S Register Conquer",_object,typeOf _object,_parameters];};
				[_object, _action, _parameters] call XfMissionObjectServerSetupConquer;
			};
			case "RESCUE":{
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 (%5) => %6",diag_tickTime,"mission_functions.sqf","S Register Rescue",_object,typeOf _object,_parameters];};
				[_object, _action, _parameters] call XfMissionObjectServerSetupRescue;
			};
			case "LEAD":{
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 (%5) => %6",diag_tickTime,"mission_functions.sqf","S Register Lead",_object,typeOf _object,_parameters];};
				[_object, _action, _parameters] call XfMissionObjectServerSetupLead;
			};
			case "ARREST":{
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 (%5) => %6",diag_tickTime,"mission_functions.sqf","S Register Arrest",_object,typeOf _object,_parameters];};
				[_object, _action, _parameters] call XfMissionObjectServerSetupArrest;
			};
			case "TAKEOVER":{
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 (%5) => %6",diag_tickTime,"mission_functions.sqf","S Register TakeOver",_object,typeOf _object,_parameters];};
				[_object, _action, _parameters] call XfMissionObjectServerSetupTakeOver;
			};
			default {
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 (%5) => %6",diag_tickTime,"mission_functions.sqf","xfMissionRegisterObject","Unknown action type",typeOf _object,_action];};
			};
		};
	};
	If (X_Client) then {
	// Executing the client side function
		Switch (toUpper _action) do {
			case "REGISTER":{
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 (%5) => %6",diag_tickTime,"mission_functions.sqf","C Register Only",_object,typeOf _object,_parameters];};
				player reveal _object;
			};
			case "DESTROY":{
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 (%5) => %6",diag_tickTime,"mission_functions.sqf","C Register Destroy",_object,typeOf _object,_parameters];};
				[_object, _action, _parameters] call XfMissionObjectClientSetupDestroy;
			};
			case "PICKUP":{
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 (%5) => %6",diag_tickTime,"mission_functions.sqf","C Register Pickup",_object,typeOf _object,_parameters];};
				[_object, _action, _parameters] call XfMissionObjectClientSetupPickup;
			};
			case "ACTIVATE":{
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 (%5) => %6",diag_tickTime,"mission_functions.sqf","C Register Activate",_object,typeOf _object,_parameters];};
				[_object, _action, _parameters] call XfMissionObjectClientSetupActivate;
			};
			case "ASSASSINATE":{
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 (%5) => %6",diag_tickTime,"mission_functions.sqf","C Register Assassinate",_object,typeOf _object,_parameters];};
				[_object, _action, _parameters] call XfMissionObjectClientSetupAssassinate;
			};
			case "CONQUER":{
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 (%5) => %6",diag_tickTime,"mission_functions.sqf","C Register Conquer",_object,typeOf _object,_parameters];};
				[_object, _action, _parameters] call XfMissionObjectClientSetupConquer;
			};
			case "RESCUE":{
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 (%5) => %6",diag_tickTime,"mission_functions.sqf","C Register Rescue",_object,typeOf _object,_parameters];};
				[_object, _action, _parameters] call XfMissionObjectClientSetupRescue;
			};
			case "LEAD":{
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 (%5) => %6",diag_tickTime,"mission_functions.sqf","C Register Lead",_object,typeOf _object,_parameters];};
				[_object, _action, _parameters] call XfMissionObjectClientSetupLead;
			};
			case "ARREST":{
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 (%5) => %6",diag_tickTime,"mission_functions.sqf","C Register Arrest",_object,typeOf _object,_parameters];};
				[_object, _action, _parameters] call XfMissionObjectClientSetupArrest;
			};
			case "TAKEOVER":{
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 (%5) => %6",diag_tickTime,"mission_functions.sqf","C Register TakeOver",_object,typeOf _object,_parameters];};
				[_object, _action, _parameters] call XfMissionObjectClientSetupTakeOver;
			};
			default {
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 (%5) => %6",diag_tickTime,"mission_functions.sqf","xfMissionRegisterObject","Unknown action type",typeOf _object,_action];};
			};
		};
	};
};

// When an action is added to an object through AddAction, an event is raised on all systems when the action is activated.
XfMissionExecuteObjectAction = {
	private ["_object","_action_id","_player","_action","_arguments","_parameters"];
	_object = _this select 0;
	_player = _this select 1;
	_action_id = _this select 2;
	_arguments = _this select 3;
	_action = _object call XfMissionGetAction;
	_parameters = _object call XfMissionGetParameters;
	
	_action_override = "";
	If (count _arguments > 0) then {_action_override = _arguments select 0};
	If (_action_override != "") then {_action = _action_override};
	// Executing the server side function
	If (X_Server) then {
		Switch (toUpper _action) do {
			case "PICKUP":{
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 => %5",diag_tickTime,"mission_functions.sqf","S Execute Pickup",_object,_parameters];};
				[_object, _action, _parameters, _player, _action_id, _arguments] call XfMissionObjectServerExecutePickup
			};
			case "ACTIVATE":{
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 => %5",diag_tickTime,"mission_functions.sqf","S Execute Activate",_object,_parameters];};
				[_object, _action, _parameters, _player, _action_id, _arguments] call XfMissionObjectServerExecuteActivate
			};
			case "RESCUE":{
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 => %5",diag_tickTime,"mission_functions.sqf","S Execute Rescue",_object,_parameters];};
				[_object, _action, _parameters, _player, _action_id, _arguments] call XfMissionObjectServerExecuteRescue
			};
			case "LEAD":{
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 => %5",diag_tickTime,"mission_functions.sqf","S Execute Lead",_object,_parameters];};
				[_object, _action, _parameters, _player, _action_id, _arguments] call XfMissionObjectServerExecuteLead
			};
			case "DROP":{
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 => %5 / %6",diag_tickTime,"mission_functions.sqf","S Execute Drop",_object,_parameters,_arguments];};
				[_object, _action, _parameters, _player, _action_id, _arguments] call XfMissionObjectServerExecuteDrop
			};
			default {
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf - XfMissionExecuteObjectAction","Action is not implemented",_action];};
			};
		};
	};
	If (X_Client) then {
	// Executing the client side function
		Switch (toUpper _action) do {
			case "PICKUP":{
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 => %5",diag_tickTime,"mission_functions.sqf","C Execute Pickup",_object,_parameters];};
				[_object, _action, _parameters, _player, _action_id, _arguments] call XfMissionObjectClientExecutePickup
			};
			case "ACTIVATE":{
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 => %5",diag_tickTime,"mission_functions.sqf","C Execute Activate",_object,_parameters];};
				[_object, _action, _parameters, _player, _action_id, _arguments] call XfMissionObjectClientExecuteActivate
			};
			case "RESCUE":{
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 => %5",diag_tickTime,"mission_functions.sqf","C Execute Rescue",_object,_parameters];};
				[_object, _action, _parameters, _player, _action_id, _arguments] call XfMissionObjectClientExecuteRescue
			};
			case "LEAD":{
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 => %5",diag_tickTime,"mission_functions.sqf","C Execute Lead",_object,_parameters];};
				[_object, _action, _parameters, _player, _action_id, _arguments] call XfMissionObjectClientExecuteLead
			};
			case "DROP":{
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 => %5 / %6",diag_tickTime,"mission_functions.sqf","C Execute Drop",_object,_parameters,_arguments];};
				[_object, _action, _parameters, _player, _action_id, _arguments] call XfMissionObjectClientExecuteDrop
			};
			default {
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 / %5 / %6",diag_tickTime,"mission_functions.sqf - XfMissionExecuteObjectAction","Action is not implemented",_action,_parameters,_arguments];};
			};
		};
	};
};

XfMissionListAvailableMissions = {
	private ["_path","_available_missions","_current_mission","_i","_found","_file_name","_file_content"];
	_path = "missions\m\";
	if (count _this > 0) then { if ((_this select 0) != "") then {_path = _this select 0 }; };
	_found = true;
	_i = 1;
	_available_missions = [];
	_current_world = getText (configFile>>"CfgWorlds">>worldName>> "worldname");
	If (d_mission_file_count != 0) then {
		while {_found} do {
			_file_name = format["%1mission%2.sqf",_path,_i];
			_found = false;
			try
			{
				_file_content = loadFile _file_name;
				DM_Mission_Name="";
				DM_Mission_Desc="";
				DM_Mission_Keywords="";
				DM_Mission_Chapters = 0;
				DM_Mission_FolderName = "";
				DM_Mission_StartTime = [0,0]; // In the format [hours, minutes];
				DM_Mission_StartRain = 0; // 0 = no rain, 1 = max rain
				DM_Mission_StartOvercast = 0; // 0 = no clouds,  1 = stormy
				DM_Mission_Maps = []; // An array of maps this mission is restricted to
				if (_file_content != "") then {
					if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 %5",diag_tickTime,"mission_functions.sqf","XfMissionListAvailableMissions","Loading file",_file_name];};
					call compile preprocessFileLineNumbers _file_name;
					If (DM_Mission_Name != "") then {
						if ((count DM_Mission_Maps <= 0) || ((DM_Mission_Maps find _current_world) >= 0)) then {
							_current_mission = [_file_name,DM_Mission_Name,DM_Mission_Desc,DM_Mission_Keywords,DM_Mission_Chapters,DM_Mission_FolderName,DM_Mission_StartTime,DM_Mission_StartRain,DM_Mission_StartOvercast];
							if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4: %5",diag_tickTime,"mission_functions.sqf","XfMissionListAvailableMissions","Loaded informations",_current_mission];};
							_available_missions set [count _available_missions, _current_mission];
						} else {
							diag_log Format["(%1)[%2]: %3: %4: %5 %6 in %7",diag_tickTime,"mission_functions.sqf","XfMissionListAvailableMissions","Mission is not compatible",DM_Mission_Name, _current_world, DM_Mission_Maps];
						};
					};
					_found = true;
				} else {
					_found = false;
				};
			}
			catch
			{
				diag_log Format["(%1)[%2]: %3: %4 %5: %6",diag_tickTime,"mission_functions.sqf","XfMissionListAvailableMissions","Failed for file",_file_name, _exception];
			};
			_i = _i + 1;
			// In case we speficied a mission count, we exit after checking all of them to avoid the error message.
			if ((d_mission_file_count > 0) and (_i > d_mission_file_count)) then { _found = false; };
		};
	};
	_available_missions;
};

XfMissionLaunchParams = {
	private ["_result_array"];
	_result_array = [];
	_result_array set [0,"EN"];
	_result_array set [1,DM_DIALOG_TIME];
	if (DM_DIALOG_WEATHER == 0) then { DM_DIALOG_WEATHER = floor(random(count d_mission_dlg_weather)) + 1};
	_result_array set [2,(d_mission_dlg_weather select DM_DIALOG_WEATHER) select 1];
	_result_array set [3,(d_mission_dlg_weather select DM_DIALOG_WEATHER) select 2];
	if (DM_DIALOG_POWER == 0) then { DM_DIALOG_POWER = floor(random(count d_mission_dlg_power)) + 1};
	_result_array set [4,(d_mission_dlg_power select DM_DIALOG_POWER) select 1];
	_result_array set [5,(d_mission_dlg_power select DM_DIALOG_POWER) select 2];
	if (DM_DIALOG_REINFORCEMENT == 0) then { DM_DIALOG_REINFORCEMENT = floor(random(count d_mission_dlg_reinforcement)) + 1};
	_result_array set [6,(d_mission_dlg_reinforcement select DM_DIALOG_REINFORCEMENT) select 1];
	if (DM_DIALOG_AIRSUPPORT == 0) then { DM_DIALOG_AIRSUPPORT = floor(random(count d_mission_dlg_airsupport)) + 1};
	_result_array set [7,(d_mission_dlg_airsupport select DM_DIALOG_AIRSUPPORT) select 1];
	if (DM_DIALOG_COMM == 0) then { DM_DIALOG_COMM = floor(random(count d_mission_dlg_communication)) + 1};
	_result_array set [8,(d_mission_dlg_communication select DM_DIALOG_COMM) select 1];
	if (DM_DIALOG_SNIPER == 0) then { DM_DIALOG_SNIPER = floor(random(count d_mission_dlg_snipers)) + 1};
	_result_array set [9,[(d_mission_dlg_snipers select DM_DIALOG_SNIPER) select 1, (d_mission_dlg_snipers select DM_DIALOG_SNIPER) select 2]];

	_result_array;
};

XfMissionParseParameters = {
	private ["_hour"];
	// Those parameters should not be set in the mission itself.
	DM_Mission_EnemyPower = 0; 
	DM_Mission_EnemyCoeff = 0;
	DM_Mission_RandomReinforcement = 0;
	DM_Mission_Snipers = [0,0];
	if (count _this > 0) then { if (str(_this select 0) != "DEFAULT") then {DM_Mission_Language = _this select 0;};};
	_hour = DM_Mission_StartTime select 0;
	if (count _this > 1) then {	if (str(_this select 1) != "DEFAULT") then {_hour = _this select 1;  if (_hour >23) then {_hour=23;};  if (_hour <0) then {_hour=0;}; };};
	DM_Mission_StartTime set [0,_hour];
	if (count _this > 2) then {	if (str(_this select 2) != "DEFAULT") then {DM_Mission_StartRain = _this select 2;  if (DM_Mission_StartRain >1) then {DM_Mission_StartRain=1;}; };};
	if (count _this > 3) then {	if (str(_this select 3) != "DEFAULT") then {DM_Mission_StartOvercast = _this select 3;  if (DM_Mission_StartOvercast >1) then {DM_Mission_StartOvercast=1;}; };};
	if (count _this > 4) then {	if (str(_this select 4) != "DEFAULT") then {DM_Mission_EnemyPower = floor(_this select 4); if (DM_Mission_EnemyPower >5) then {DM_Mission_EnemyPower=5;}; };};
	if (count _this > 5) then {	if (str(_this select 5) != "DEFAULT") then {DM_Mission_EnemyCoeff = floor(_this select 5); if (DM_Mission_EnemyCoeff >3) then {DM_Mission_EnemyCoeff=3;}; };};
	if (count _this > 6) then {	if (str(_this select 6) != "DEFAULT") then {DM_Mission_RandomReinforcement = floor(_this select 6); if (DM_Mission_RandomReinforcement >6) then {DM_Mission_RandomReinforcement=6;}; };};
	if (count _this > 7) then {	if (str(_this select 7) != "DEFAULT") then {DM_Mission_AirSupport = _this select 7; if (DM_Mission_AirSupport >5) then {DM_Mission_AirSupport=5;}; };};
	if (count _this > 8) then {	if (str(_this select 8) != "DEFAULT") then {KRON_UPS_sharedist = _this select 8; if (KRON_UPS_sharedist < 100) then {KRON_UPS_sharedist=100;}; if (KRON_UPS_sharedist > 900) then {KRON_UPS_sharedist=900;}; };};
	if (count _this > 9) then {	if (str(_this select 9) != "DEFAULT") then {DM_Mission_Snipers = _this select 9; };};
};

XfMissionInitializeTeleporter = {
	if (isServer) then {
		if (!isNil "MissionStartTeleporter") then {
			_initialized = MissionStartTeleporter getVariable "InitialPos";
			if (isnil "_initialized") then {
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3",diag_tickTime,"mission_functions.sqf","Creating the trigger for teleporting to the Mission Start marker"]; };
				// This is the trigger which will close the hangar when the teleporter is not far enough
				_position = getPos MissionStartTeleporter;
				_angle = getDir MissionStartTeleporter;
				MissionStartTeleporter setVariable ["InitialPos",_position,True];
				MissionStartTeleporter setVariable ["InitialDir",_angle,True];
				// This is the trigger which will teleport the player and his vehicle and crew
				_trigger = [_position, [14,17,_angle,True], [d_own_side, "PRESENT", true], ["('LandVehicle' countType thislist  > 0) && count (thislist unitsBelowHeight 1) > 0", "diag_log 'Teleport Trigger Activated'; ['d_teleport',thislist] call XNetCallEvent;", ""]] call XfCreateTrigger;
				MissionStartTeleporter setVariable ["Trigger",_trigger,True];
				if (d_own_side == "WEST") then {
					_trigger = [_position, [14,17,_angle,True], ["GUER", "PRESENT", true], ["('LandVehicle' countType thislist  > 0) && count (thislist unitsBelowHeight 1) > 0", "diag_log 'Teleport Trigger Activated'; ['d_teleport',thislist] call XNetCallEvent;", ""]] call XfCreateTrigger;
					MissionStartTeleporter setVariable ["AltTrigger",_trigger,True];
				};
				
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3",diag_tickTime,"mission_functions.sqf","Creating the teleport hangar"]; };
				_teleport_hangar = createVehicle ["Land_SS_hangar",(_position ),[],0,'CAN_COLLIDE'];
				_teleport_hangar setPos _position;
				_teleport_hangar setDir (_angle + 180);
				_teleport_hangar setVectorUp [0,0,1];
				_teleport_hangar allowDamage false;
				MissionStartTeleporter setVariable ["Hangar",_teleport_hangar,True];
				
				MissionStartTeleporter setVariable ["Barriers",[],True];
				[[0,0,0], [0,0,0,false], ["NONE", "PRESENT", true], ["(MissionStartTeleporter distance (MissionStartTeleporter getVariable 'Hangar')) < 100", "if (d_mission_debug_messages) then {diag_log 'Teleport Hangar Closed'}; [true] call XfMissionCloseTeleporterHangar;", "if (d_mission_debug_messages) then {diag_log 'Teleport Hangar Opened'};  [false] call XfMissionCloseTeleporterHangar;"]] call XfCreateTrigger;
				
				// d_teleport_hangar_closing_barriers = [[objNull,[4499.11,10776.9,0]],[objNull,[4501.74,10772.4,0]],[objNull,[4504.34,10767.9,0]],[objNull,[4506.86,10763.5,0]]];
				// [[0,0,0], [0,0,0,false], ["NONE", "PRESENT", true], ["(MissionStartTeleporter distance d_teleport_hangar_trigger_pos) < 500", "diag_log 'Teleport Hangar Closed'; _i=0; [true] call XfMissionCloseTeleporterHangar; { _obj= createVehicle ['Land_RedWhiteBarrier',(_x select 1),[],0,'CAN_COLLIDE']; _obj setDir 240; _x set [0,_obj]; d_teleport_hangar_closing_barriers set[_i,_x]; _i=_i+1;}foreach d_teleport_hangar_closing_barriers;", "diag_log 'Teleport Hangar Opened'; _i=0; { _obj = _x select 0; if (!(isNull _obj)) then {deleteVehicle _obj}; _x set [0,objNull]; d_teleport_hangar_closing_barriers set[_i,_x]; _i=_i+1;}foreach d_teleport_hangar_closing_barriers;"]] call XfCreateTrigger;
			} else {
				diag_log Format["(%1)[%2]: %3",diag_tickTime,"mission_functions.sqf","Mission Teleporter already initialized"];
			};
		};
	};
};


/// @FUNCTION
/// @NAME XfMissionCancelExecuting
/// @PACKAGE MissionFramework
/// @DESC Stops the currently executing mission.
/// @ACCESS Server
/// @MP False
/// @PARAM Player The player who canceled the mission.
/// @RETURN Nothing
/// @REQUIRE Domination Template
/// @REMARK You need to call it through d_s_mexecute event.
/// @EXAMPLE ['d_s_mexecute',['XfMissionCancelExecuting',[player]]] call XNetCallEvent;
XfMissionCancelExecuting = {
	_player = _this select 0;
	Format["%1 cancelled the currently executing mission.",name _player] call XfMissionMessageToAll;
	// This calls the Canceling of the mission itself. It will execute any specific tasks
	[] call XfMissionCancel;
};

/// @FUNCTION
/// @NAME XfMissionPlaceStartTeleporter
/// @PACKAGE MissionFramework
/// @DESC Places the mission Start teleporter on the map.
/// @ACCESS Public
/// @MP True
/// @PARAM Position The position where to place the teleporter. If you give an empty array, it automatically returns to the teleport hangar. The position can be given in the format [pos],angle.
/// @RETURN Nothing
/// @REQUIRE Domination Template
/// @REMARK
/// @EXAMPLE [[pos],angle] call XfMissionPlaceStartTeleporter
XfMissionPlaceStartTeleporter = {
	if (isServer) then {
		private ["_position","_angle"];
		_position = _this;
		_angle = 0;
		if (typeName (_this select 0) == "ARRAY") then {
			_position = _this select 0;
			if (count _this > 1) then { _angle = _this select 1; };
		};
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionPlaceStartTeleporter",_this];};
		if (!isNil "MissionStartTeleporter") then {
			If (count _position == 0) then {
				_position = MissionStartTeleporter getVariable "InitialPos";
				_angle = MissionStartTeleporter getVariable "InitialDir";
			};
			MissionStartTeleporter setPos _position;
			MissionStartTeleporter setDir _angle;
		};
	} else {
		["d_s_mexecute", ["XfMissionPlaceStartTeleporter",_this]] call XNetCallEvent;
	};
};
XfMissionCloseTeleporterHangar = {
	private ["_close","_barriers","_i","_hangar","_barrier","_pos","_angle"];
	_close = _this select 0;
	If (isServer) then {
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionCloseTeleporterHangar",_this];};
		if (!isNil "MissionStartTeleporter") then {
			_barriers = MissionStartTeleporter getVariable "Barriers";
			if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: Barriers: %4",diag_tickTime,"mission_functions.sqf","XfMissionCloseTeleporterHangar",_barriers];};
			if (!_close) then {
				if (count _barriers > 0) then {
					{
						deleteVehicle _x;
					} foreach _barriers;
					MissionStartTeleporter setVariable ["Barriers",[],True];
				};
			} else {
				_hangar = MissionStartTeleporter getVariable "Hangar";
				if (! isnil "_hangar") then {
					_angle = getDir _hangar;
					for "_i" from 0 to 3 do {
						_barrier = objNull;
						if (count _barriers > _i) then {
							_barrier = _barriers select _i;
						};
						if ( isNull _barrier) then {
							_pos = _hangar modelToWorld [(-7.9 + 5 * _i),-21,-5.6];
							_barrier = createVehicle ["Land_RedWhiteBarrier",_pos,[],0,"CAN_COLLIDE"];
							_barrier setPos _pos;
							_barrier setDir (_angle + 180);
							_barrier allowDamage false;
							_barriers set [_i,_barrier];
						};
					};
				};
			};
			MissionStartTeleporter setVariable ["Barriers",_barriers,True];
		};
	} else {
		diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionCloseTeleporterHangar","Executed on non server"];
	};
};

/// @FUNCTION
/// @NAME XfMissionRandomStartTeleporter
/// @PACKAGE MissionFramework
/// @DESC Places the mission Start teleporter on the map on a random position around the target zone.
/// @ACCESS Public
/// @MP True
/// @PARAM Position The position around which to place the teleporter. If you give an empty array, it automatically returns to the teleport hangar.
/// @RETURN Array Position Where the teleporter will be placed + Angle
/// @REQUIRE Domination Template
/// @REMARK
/// @EXAMPLE [[pos]] call XfMissionRandomStartTeleporter
XfMissionRandomStartTeleporter = {
	private ["_center","_spot","_search_try","_roads","_angle"];
	// We search a spot to place the teleporter which is around the town on a road (to avoid mountains and forests)
	_center = _this select 0;
	_spot = [];
	_angle = 0;
	if (count _center > 0) then {
		_search_try = 50;
		while {_search_try > 0 && (count _spot == 0)} do {
			_spot = [_center, 2000, 1600] call XfGetRanPointCircle;
			if ((count _spot) > 0) then {
				_roads = (_spot nearRoads 200);
				if (count _roads > 0) then {
					{
						_spot = (getPos _x) findEmptyPosition [0,30,"M1A2_TUSK_MG"];
						if (count _spot == 3) exitWith {};
					} foreach _roads;
				} else {
					_spot = [];
				};
			};
			_search_try = _search_try - 1;
		};
		if (count _spot > 0) then {
			_angle =([_spot, _random_town_pos] call XfDirTo);
		};
	};
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionPlaceStartTeleporter",_this];};
	if (count _spot > 0) then {
		if (isServer) then {
			[_spot,_angle] call XfMissionPlaceStartTeleporter;
		} else {
			["d_s_mexecute", ["XfMissionPlaceStartTeleporter",[_spot,_angle]]] call XNetCallEvent;
		};
	};
	[_spot,_angle];
};



XfMissionGetAction = {
	private ["_action"];
	_action = _this getVariable "mission_action_type";
	if (isNil "_action") then { _action = "UNKNOWN"; };
	_action;
};
XfMissionSetAction = {
	private ["_object","_action","_existing","_params"];
	_object = _this select 0;
	_action = _this select 1;
	if (isNil "_action") then { _action = "UNKNOWN"; };
	_existing = toUpper(_object call XfMissionGetAction);
	if (_existing != "UNKNOWN") then {
		_params = _object call XfMissionGetParameters;
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 %5",diag_tickTime,"mission_functions.sqf","XfMissionSetAction","The object had an action!",[_existing,_params]];};
	};
	_object setVariable ["mission_action_type",_action,true];
};
XfMissionGetParameters = {
	private ["_parameters"];
	_parameters = _this getVariable "mission_action_parameters";
	if (isNil "_parameters") then { _parameters = []; };
	_parameters;
};
XfMissionSetParameters = {
	private ["_object","_parameters"];
	_object = _this select 0;
	_parameters = _this select 1;
	if (isNil "_parameters") then { _parameters = []; };
	_object setVariable ["mission_action_parameters",_parameters,true];
};
XfMissionAddObject = {
	private ["_JIPH_MissionObjects"];
	diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionAddObject",_this, (JIP_HINV getVariable "G_MISSION_OBJECTS")];
	_JIPH_MissionObjects = JIP_HINV getVariable "G_MISSION_OBJECTS";
	If (isNil "_JIPH_MissionObjects") then {
		JIP_HINV setVariable ["G_MISSION_OBJECTS",[],true];
		_JIPH_MissionObjects = [];
	};
	if (_this in _JIPH_MissionObjects) then {
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 (%5)",diag_tickTime,"mission_functions.sqf","XfMissionAddObject","The object already exists!",(_this getVariable "MissionObject")];};
	} else {
		_JIPH_MissionObjects = JIP_HINV getVariable "G_MISSION_OBJECTS";
		_this setVariable ["MissionID",0,true];
		_this setVariable ["MissionObject",count _JIPH_MissionObjects,true];
		_JIPH_MissionObjects set [count _JIPH_MissionObjects, _this];
		JIP_HINV setVariable ["G_MISSION_OBJECTS",_JIPH_MissionObjects,True];
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 (%5)",diag_tickTime,"mission_functions.sqf","XfMissionAddObject","Added the object to the shared list!",(count _JIPH_MissionObjects)];};
	};
};
XfMissionInitializeObjects = {
	private ["_JIPH_MissionObjects"];
	_JIPH_MissionObjects = JIP_HINV getVariable "G_MISSION_OBJECTS";
	If (!isNil "_JIPH_MissionObjects") then {
		{
			_x call XfMissionRegisterObject;
		} foreach _JIPH_MissionObjects;
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4:%5",diag_tickTime,"mission_functions.sqf","XfMissionInitializeObjects","Initialized objects",count _JIPH_MissionObjects];};
	} else {
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionInitializeObjects","No objects to initialize!"];};
	};
};
XfMissionLoadPlayerActions = {
	private ["_i","_current_action","_drop_action","_action_title","_action_params","_action_priority","_action_condition","_action_showwindow"];
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3",diag_tickTime,"mission_functions.sqf","XfMissionLoadPlayerActions","Start waiting"];};
	if (count d_mission_actions_list > 0) then {
		// We wait for the player to be alive, otherwise we'll just add the actions to his corpse... would be sad, no?
		waituntil {alive player};
		for [{_i=0},{_i < count d_mission_actions_list},{_i = _i + 1}] do {
			_current_action = d_mission_actions_list select _i;
			_drop_action = _current_action select 0;
			_action_title = _current_action select 1;
			_action_params = _current_action select 2;
			_action_priority = _current_action select 3;
			_action_condition = _current_action select 4;
			_action_showwindow = false;
			if (_action_priority > 5) then {_action_showwindow = true;};
			if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionLoadPlayerActions","Adding action",_current_action];};
			_drop_action = player addAction [_action_title, "missions\process_action.sqf", _action_params,_action_priority,_action_showwindow,True,"",_action_condition];
			_current_action set [0, _drop_action];
			// We overwrite the old value with the updated one. It's mandatory for cleanup
			d_mission_actions_list set [_i, _current_action];
		};
	};
};


/// @FUNCTION
/// @NAME XfMissionTask
/// @DESC 
/// @ACCESS Public
/// @MP True
/// @PARAM String The Task Unique Name, preferably without spaces
/// @PARAM String The Task title, which you see in the list of tasks on the map -> briefing
/// @PARAM String The Task description (note that you can use <br/> to go to the next line.
/// @PARAM String The Task Waypoint Description
/// @PARAM Position The position of waypoint (to move the map view automatically). Give an empty array to ignore it.
/// @PARAM String The status of the task: "Succeeded", "Failed", "Canceled", "Created", "Assigned"
/// @PARAM String The name of a JIP var which indicates the success of the mission @DEFAULT Empty. Do not auto-check for success
/// @PARAM String The name of a JIP var which indicates the failure of the mission @DEFAULT Empty. Do not auto-check for failure
/// @PARAM String The name of a JIP var which indicates the failure of the mission but inverted (True = not failed, False = failed) @DEFAULT Empty. Do not auto-check for inverted failure
/// @RETURN Nothing
/// @REQUIRE Domination Template
/// @REMARK
/// @EXAMPLE ["taskUniqueName", "Task Title", "Task Description", "Task Waypoint Desc", [TaskWP], "TaskStatus","TaskSuccessVar","TaskFailureVar"] call XfMissionTask
/// @EXAMPLE ["FindTheMoney","Find Olaf's Money", "You need to find Olaf's money in Msta before the russian special forces!","",[],"Created"] call XfMissionTask
XfMissionTask = {
	["d_all_mtask",_this] call XNetCallEvent;
};
XfMissionUpdateTask = {
	private ["_taskUnique","_taskTitle","_taskDesc","_taskWPDesc","_taskWPPos","_taskStatus","_unitTasks","_taskPos","_currentItem","_foundTask","_i","_taskSuccessVar","_taskFailedVar","_taskFailedInvertVar"];
	_taskUnique = _this select 0;
	_taskTitle = _this select 1;
	_taskDesc = _this select 2;
	_taskWPDesc = _this select 3;
	_taskWPPos = _this select 4;
	_taskStatus = _this select 5;
	_taskSuccessVar= "";
	if (count _this > 6) then {	_taskSuccessVar = _this select 6; };
	_taskFailedVar= "";
	if (count _this > 7) then {	_taskFailedVar = _this select 7; };
	_taskFailedInvertVar= "";
	if (count _this > 8) then {	_taskFailedInvertVar = _this select 8; };
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4: %5",diag_tickTime,"mission_functions.sqf","XfMissionUpdateTask","Updating task",_this];};
	if (_taskUnique == "") exitWith {};
	
	// 1. We create/update the task in the JIP variable, this allows for recreating tasks on spawn/respawn.
	If (X_Server) then {
		_taskPos = -1;
		_unitTasks = ["current_mission_tasks",[]] call XNetGetJIP;
		if (!isNil "_unitTasks") then {
			if (count _unitTasks > 0) then {
				for [{_i = 0;},{_i < (count _unitTasks)},{_i = _i + 1;}] do {
					_currentItem = _unitTasks select _i;
					if (str(_currentItem select 0) == str(_taskUnique)) exitWith {_taskPos = _i;};
				};
			};
		} else {
			_unitTasks = [];
		};
		if (_taskPos < 0) then {
			_foundTask = [];
			_foundTask set [0, _taskUnique];
			_foundTask set [1, _this];
			_unitTasks set [count _unitTasks, _foundTask];
			if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4: %5",diag_tickTime,"mission_functions.sqf","XfMissionUpdateTask","Adding task",_taskUnique];};
		} else {
			_foundTask = _unitTasks select _taskPos;
			// We update the infos
			_foundTask set [1, _this];
			_unitTasks set [_taskPos, _foundTask];
		};
		["current_mission_tasks",_unitTasks] call XNetSetJIP;
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 (%5, %6)",diag_tickTime,"mission_functions.sqf","XfMissionUpdateTask","Updated server task",_taskUnique, count _unitTasks];};
	};
	
	// 2. We create/update the task on the client side
	If (X_Client) then {
		_unitTasks = player getVariable "mission_created_tasks";
		if (isNil "_unitTasks") then { _unitTasks = []; player setVariable ["mission_created_tasks",_unitTasks];};
		_taskPos = -1;
		if (count _unitTasks > 0) then {
			for [{_i = 0;},{_i < (count _unitTasks)},{_i = _i + 1;}] do {
				_currentItem = _unitTasks select _i;
				if (str(_currentItem select 0) == str(_taskUnique)) exitWith {_taskPos = _i;};
			};
		};
		If (_taskPos < 0) then {
			_foundTask = player createSimpleTask [_taskUnique];
			_unitTasks set [count _unitTasks, [_taskUnique,_foundTask]];
			_taskPos = count _unitTasks;
		} else {
			_foundTask = (_unitTasks select _taskPos) select 1;
			if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4=%5 (%6)",diag_tickTime,"mission_functions.sqf","XfMissionUpdateTask","Task State",str(taskState _foundTask),_taskUnique];};
			if (str(taskState _foundTask) == "None") then { 
				_foundTask = player createSimpleTask [_taskUnique];
			};
		};
		If (_taskTitle != "") then {
			_foundTask setSimpleTaskDescription [_taskDesc,_taskTitle,_taskWPDesc];
		};
		if (count _taskWPPos > 0) then {
			_foundTask setSimpleTaskDestination _taskWPPos;
		};
		_foundTask setTaskState _taskStatus;
		_unitTasks set [_taskPos, [_taskUnique, _foundTask]];
		player setVariable ["mission_created_tasks",_unitTasks];
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4=%5 (%6)",diag_tickTime,"mission_functions.sqf","XfMissionUpdateTask","Adding task to client",_taskUnique, count _unitTasks];};
		hintsilent "A task was updated!";
		[_taskUnique,_taskSuccessVar,_taskFailedVar,_taskFailedInvertVar] spawn {
			private ["_taskName","_success","_failure","_failureInvert"];
			_taskName = _this select 0;
			_success = _this select 1;
			_failure = _this select 2;
			_failureInvert = _this select 3;
			if ((_success != "") OR (_failure !="") OR (_failureInvert != "")) then {
				waituntil {sleep 15; 
					((if (_success != "") then {([_success,false] call XNetGetJIP)} else {false}) OR (if (_failure != "") then {([_failure,false] call XNetGetJIP)} else {false})OR (if (_failureInvert != "") then {(!([_failureInvert,true] call XNetGetJIP))} else {false}));
				};
				if ((_success != "") AND ([_success,false] call XNetGetJIP)) then {
					[_taskName,"", "","",[],"Succeeded"] call XfMissionTask;
				} else {
					if ((_failure != "") AND ([_failure,false] call XNetGetJIP)) then {
						[_taskName,"", "","",[],"Failed"] call XfMissionTask;
					} else {	
						if ((_failureInvert != "") AND (!([_failureInvert,true] call XNetGetJIP))) then {
							[_taskName,"", "","",[],"Failed"] call XfMissionTask;
						};
					};
				};
			};
		};
	};
};
XfMissionLoadTasks = {
	private ["_taskUnique","_taskTitle","_taskDesc","_taskWPDesc","_taskWPPos","_taskStatus","_unitTasks","_taskPos","_currentItem","_foundTask","_i"];
	_unitTasks = ["current_mission_tasks",[]] call XNetGetJIP;
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4: %5",diag_tickTime,"mission_functions.sqf","XfMissionLoadTasks","Loading",count _unitTasks];};
	if (!isNil "_unitTasks") then {
		if (count _unitTasks > 0) then {
			for [{_i = 0;},{_i < (count _unitTasks)},{_i = _i + 1;}] do {
				_currentItem = _unitTasks select _i;
				_currentItem select 1 call XfMissionUpdateTask;
			};
		};
	};
};
// Form: [serverOnly,[marker parameters]] call XfMissionMarker
// Ex: [false,[marker parameters]] call XfMissionMarker
XfMissionMarker = {
	["d_all_mmarker",_this] call XNetCallEvent;
};
// Creates, updates, deletes markers on all systems
// Parameters of the form: [serverOnly,[marker parameters]]
XfMissionUpdateMarker = {
	private ["_is_server_only","_marker_params","_marker_name","_delete_marker","_i","_marker_pos","_current_marker","_mission_markers"];
	_is_server_only = _this select 0;
	_marker_params = _this select 1;
	_marker_name = _marker_params select 0;
	_delete_marker= (count _marker_params == 1);
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 => %5",diag_tickTime,"mission_functions.sqf","XfMissionUpdateMarker","Adding marker",_this];};
	If (X_Server) then {
		// If the marker needs to be synchronized in JIP, we add/update it in the JIP list.
		if (!_is_server_only) then {
			_mission_markers = ["current_mission_markers",[]] call XNetGetJIP;
			_marker_pos = -1;
			If ((count _mission_markers)>0) then {
				for [{_i = 0},{_i < count _mission_markers},{_i = _i + 1}] do {
					_current_marker = _mission_markers select _i;
					if (str(_current_marker select 0) == str(_marker_name)) exitWith{_marker_pos = _i};
				};
			};
			if (_marker_pos < 0) then {
				_mission_markers set [count _mission_markers, [_marker_name,_marker_params]];
			} else {
				_mission_markers set [_marker_pos, [_marker_name,_marker_params]];
			};
			["current_mission_markers",_mission_markers] call XNetSetJIP;
			if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 (%5)",diag_tickTime,"mission_functions.sqf","XfMissionUpdateMarker","Added marker to JIP",(count _mission_markers)];};
		};
		// We add this check for when we test the mission in the editor. Then the marker got create on the Client anyway.
		if ((!X_Client) OR _is_server_only) then {
			_marker_created_pos = -1;
			if (count d_mission_marker_list	> 0) then {
				for [{_i = 0},{_i < count d_mission_marker_list},{_i = _i + 1}] do {
					_current_marker = d_mission_marker_list select _i;
					if (str(_current_marker) == str(_marker_name)) exitWith{_marker_created_pos = _i};
				};			
			};
			if (_delete_marker) then {
				If (_marker_created_pos > 0) then {
					d_mission_marker_list set [count d_mission_marker_list,"XfXXDEL"];
					d_mission_marker_list = d_mission_marker_list - ["XfXXDEL"];
					if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 (%5)",diag_tickTime,"mission_functions.sqf","XfMissionUpdateMarker","Removed from server list",_marker_name];};
				};			
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 (%5)",diag_tickTime,"mission_functions.sqf","XfMissionUpdateMarker","Deleting server marker",_marker_name];};
				deleteMarkerLocal _marker_name;
			} else {
				If (_marker_created_pos < 0) then {
					_marker_params call XfCreateMarkerLocal;
					d_mission_marker_list set [count d_mission_marker_list,_marker_name];
					if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 (%5)",diag_tickTime,"mission_functions.sqf","XfMissionUpdateMarker","Creating server marker",_marker_name];};
				} else {		
					_marker_params call XfUpdateMarkerLocal;
					if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 (%5)",diag_tickTime,"mission_functions.sqf","XfMissionUpdateMarker","Updating server marker",_marker_name];};
				};			
			};
		};
	};
	If (X_Client AND (!_is_server_only)) then {
		_marker_created_pos = -1;
		if (count d_mission_marker_list	> 0) then {
			for [{_i = 0},{_i < count d_mission_marker_list},{_i = _i + 1}] do {
				_current_marker = d_mission_marker_list select _i;
				if (str(_current_marker) == str(_marker_name)) exitWith{_marker_created_pos = _i};
			};			
		};
		if (_delete_marker) then {
			If (_marker_created_pos > 0) then {
				d_mission_marker_list set [count d_mission_marker_list,"XfXXDEL"];
				d_mission_marker_list = d_mission_marker_list - ["XfXXDEL"];
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 (%5)",diag_tickTime,"mission_functions.sqf","XfMissionUpdateMarker","Removed from client list",_marker_name];};
			};			
			if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 (%5)",diag_tickTime,"mission_functions.sqf","XfMissionUpdateMarker","Deleting client marker",_marker_name];};
			deleteMarkerLocal _marker_name;
		} else {
			If (_marker_created_pos <= 0) then {
				_marker_params call XfCreateMarkerLocal;
				d_mission_marker_list set [count d_mission_marker_list,_marker_name];
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 (%5)",diag_tickTime,"mission_functions.sqf","XfMissionUpdateMarker","Creating client marker",_marker_name];};
			} else {
				_marker_params call XfUpdateMarkerLocal;
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 (%5)",diag_tickTime,"mission_functions.sqf","XfMissionUpdateMarker","Updating client marker",_marker_name];};
			};			
		};
	};
};
XfMissionLoadMarkers = {
	private ["_taskUnique","_taskTitle","_taskDesc","_taskWPDesc","_taskWPPos","_taskStatus","_unitTasks","_taskPos","_currentItem","_foundTask"];
	_missionMarkers = ["current_mission_markers",[]] call XNetGetJIP;
	if (!isNil "_missionMarkers") then {
		if (count _missionMarkers > 0) then {
			for [{_i = 0;},{_i < (count _missionMarkers)},{_i = _i + 1;}] do {
				_currentItem = _missionMarkers select _i;
				[false, _currentItem select 1] call XfMissionUpdateMarker;
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 (%5)",diag_tickTime,"mission_functions.sqf","XfMissionLoadMarkers","Adding marker",_marker_name];};
			};
		};
	};
};

// This function creates a trigger which can later be used to know if a side is detected by another in a particular zone.
// You can use this to call for reinforcement, etc.
// Ex: [[123,321,0],200,"WEST","EAST","West_Has_Been_Detected"] call XfMissionSideDetected
XfMissionSideDetected = {
	private ["_center","_radius","_detected_side","_detected_by_side","_mission_variable"];
	if (typename _this != "ARRAY") exitWith {if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionSideDetected","Expected Array Parameter"];};};
	if (count _this < 5) exitWith{if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionSideDetected","Not enough parameters"];};};
	
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionSideDetected",_this];};
	_center = _this select 0;
	_radius = _this select 1;
	_detected_side = _this select 2;
	_detected_by_side = _this select 3;
	_mission_variable = _this select 4;
	
	If (count _center <= 2) exitWith {if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionSideDetected","Expected a position"];};};
	if (_radius < 20) then {_radius = 20;};
	If (!(_detected_side in ["WEST","EAST","GUER","CIV"])) exitWith {if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionSideDetected","Side is unknown"];};};
	If (!(_detected_by_side in ["WEST","EAST","GUER","CIV"])) exitWith {if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionSideDetected","Side is unknown"];};};
	If (_mission_variable == "") exitWith {if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionSideDetected","No variable to update"];};};
	
	[_mission_variable,false] call XNetSetJIP;
	// Old working using the detected By, but it's very slow to detect anything...
	// _trigger = [_center, [_radius,_radius,0,False], [_detected_side, Format["%1 D",_detected_by_side], true], ["this", Format["['%1',True] call XNetSetJIP; if (d_mission_debug_messages) then { diag_log 'XfMissionSideDetected: Side dectected %2';",_mission_variable,_detected_side], Format["['%1',False] call XNetSetJIP; if (d_mission_debug_messages) then { diag_log 'XfMissionSideDetected: Side not dectected anymore %2';",_mission_variable,_detected_side]]] call XfCreateTrigger;
	
	// The working of the trigger is the following:
	//  1) It returns all units of the side which should detect something.
	//  2) It controls if those units have known enemies in their vicinity
	//  3) If they do, we check what they know about them
	//  4) If they know enough, we update the variable
	_detected_side	= Switch (_detected_side) do {
		case "WEST":{west};
		case "EAST":{east};
		case "GUER":{resistance};
		case "CIV":{civilian};
		default{east};
	};
	_trigger = [_center, [_radius,_radius,0,False], [_detected_by_side, "PRESENT", true], ["this", "", ""]] call XfCreateTrigger;
	d_mission_trigger_list set [count d_mission_trigger_list, _trigger];
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 (%5,%6,%7,%8,%9,%10)",diag_tickTime,"mission_functions.sqf","XfMissionSideDetected","Added Trigger",_trigger,_center,_radius,_detected_side, _detected_by_side, _mission_variable];};

	[_trigger,_mission_variable,_detected_side] spawn {
		private ["_trigger","_detected_side","_variable","_unit_present","_units"];
		_trigger = _this select 0;
		_variable = _this select 1;
		_detected_side = _this select 2;
		// Before we start looking, we wait that unit of the awaited side are present.
		waituntil {triggeractivated _trigger};
		_unit_present = true;
		while {_unit_present} do {
			if (isNull _trigger) exitWith {};
			_units = (list _trigger);
			_unit_present = (count _units > 0);
			if (_unit_present) then {
				[list _trigger,_variable,_detected_side] call XfMissionSideDetection;
				sleep 5;
			};
		};
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 (%5)",diag_tickTime,"mission_functions.sqf","XfMissionSideDetected","No more units",_trigger];};
	};
};
// Internal use
XfMissionSideDetection = {
	private ["_units","_variable","_detect_side","_unit","_enemy","_detected","_initial"];
	_units = _this select 0;
	if (isnil "_units") then {_units = [];};
	_variable = _this select 1;
	_detect_side = _this select 2;
	_initial = [_variable,false] call XNetGetJIP;
	if (isNil "_initial") then {_initial = false;};
	_detected = false;
	_enemy = objNull;
	_unit = objNull;
	// if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionSideDetection",_this];};
	{
		_unit = _x;
		// We only check leaders
		if (_unit == leader (group (vehicle _unit))) then {
			_enemy = _unit findNearestEnemy _unit;
			if (!isnull _enemy) then {
				if ((_unit knowsAbout _enemy) >= 0.4 && (side _enemy == _detect_side)) exitWith { _detected = true; };
			};
		};
		if (_detected) exitWith {};
	} foreach _units;
	[_variable,_detected] call XNetSetJIP;
	if ((_initial || _detected) && !(_initial && _detected)) then {
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 unit:%5  lead:%6  know:%7  var:%8  val:%9",diag_tickTime,"mission_functions.sqf","XfMissionSideDetection","Status Changed",_enemy,_unit,(_unit knowsAbout _enemy),_variable,_detected];};
	};
};

/// @FUNCTION
/// @NAME XfMissionMessageToAll
/// @DESC Sends a message to all players
/// @ACCESS Public
/// @MP True
/// @PARAM String The message to send to all players
/// @RETURN Nothing
/// @REQUIRE 
/// @REMARK
/// @EXAMPLE [_msg] call XfMissionMessageToAll;
XfMissionMessageToAll = {
	private "_msg";
	_msg = _this;
	if (typeName _msg == "ARRAY") then { _msg = _msg select 0;};
	["d_msg",[_msg,"all",objNull,"hq"]] call XNetCallEvent;
};

// Displays the loaded mission and time for all players
XfMissionStartScreen = {
	private ["_mission_name","_newTime","_code","_display_time"];
	_mission_name = _this select 0;
	_newTime = _this select 1;
	_display_time = _newTime call XfMissionDisplayDate;
	_code = Format["titleText [""Starting Mission\n\n~ %2 ~\n\n%3"",""BLACK""]; setDate %1; titleFadeOut 10;",_newTime,_mission_name,_display_time];
	// We update all systems
	["d_all_exec",[_code]] call XNetCallEvent;
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: old:%4  new:%5  mission:%6",diag_tickTime,"mission_functions.sqf","XfMissionStartScreen",date,_newTime,_mission_name];};
};


//Giving the hour and the minutes, it returns the next matching date.
// Say we are the 21st of Jan at 18:00 and you ask 14:30, we don't want to go back so it will return the 22 at 14:30.
XfMissionGetDate = {
	private ["_hour","_minute","_newTime"];
	_hour = _this select 0;
	_newTime = date;
	// By default we take the current minutes.
	_minute = _newTime select 4;
	if (count _this > 1) then {	_minute = _this select 1; };
	// If the current hour is bigger than the target hour, this means we need to jump to the next day
	if ((_newTime select 3) > _hour) then {
		_newTime set [2, ((_newTime select 2) + 1)]
	} else {
		// TODO: factor in the minute changes. For example, it is now 14:55 and you ask 14:30... for now it will run back in time :)
	};
	_newTime set [3, _hour];
	_newTime set [4, _minute];
	_newTime;
};

// Changes the date and time on all systems. The input parameters is a date in Array Format
XfMissionSetDate = {
	private ["_newTime","_code","_display_time"];
	_newTime = _this;
	_display_time = _newTime call XfMissionDisplayDate;
	//_code = Format["titleText ['%2','BLACK']; setDate %1; titleFadeOut 6;",_newTime,_display_time];
	_code = Format["setDate %1;",_newTime];
	// We update all systems
	["d_all_exec",[_code]] call XNetCallEvent;
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: old:%4  new:%5",diag_tickTime,"mission_functions.sqf","XfMissionSetDate",date,_newTime];};
};

XfMissionSetWeather = {
	private ["_delay","_rain","_overcast", "_fog","_code"];
	_delay = _this select 0;
	_rain = rain;
	if (count _this > 1) then {_rain = _this select 1;};
	_overcast = overcast;
	if (count _this > 2) then {_overcast = _this select 2;};
	_fog = fog;
	if (count _this > 3) then {_fog = _this select 3;};
	_code = Format["%1 setRain %2; %1 setOvercast %3; sleep 5; %1 setFog %4;",_delay,_rain,_overcast,_fog];
	// We update all systems
	["d_all_exec",[_code]] call XNetCallEvent;
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: rain:%4  overcast:%5   delay:%6",diag_tickTime,"mission_functions.sqf","XfMissionSetWeather",_rain,_overcast,_delay];};
};

XfMissionDisplayDate = {
	private ["_date_array","_month","_hour","_minute","_date"];
	_date_array = _this;
	_month = ["","Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"] select (_date_array select 1);
	_hour = _date_array select 3;
	_minute = _date_array select 4;
	If (_hour < 10) then {_hour = Format["0%1",_hour];};
	If (_minute < 10) then {_minute = Format["0%1",_minute];};
	_date = format["%1 %2 %3 at %4:%5",_newTime select 2, _month, _newTime select 0,_hour, _minute];
	_date;
};

XfMissionCurrentPosition = {
	private ["_pos","_radius","_title"];
	_pos = _this select 0;
	_radius = _this select 1;
	_title = _this select 2;
	["current_mission_position",[_pos,_radius,_title]] call XNetSetJIP;
};

XfMissionCleanEvent = {
	private ["_type","_params","_forced"];
	_type = "ALL";
	if (count _this > 0) then {_type = _this select 0;};
	_params = [];
	if (count _this > 1) then {_params = _this select 1;};
	_forced = false;
	if (count _params > 0) then {_forced = _params select 0;};
	_type = ToUpper(_type);
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionCleanEvent",_this];};
	Switch (_type) do {
		case "ALL":{
			[_forced] call XfMissionCleanExecuteAll;
		};
		case "OBJECTS":{
			[_forced] call XfMissionCleanExecuteObjects;
		};
		case "MARKERS":{
			[_forced] call XfMissionCleanExecuteMarkers;
		};
		case "TRIGGERS":{
			[_forced] call XfMissionCleanExecuteTriggers;
		};
		case "TASKS":{
			[_forced] call XfMissionCleanExecuteTasks;
		};
		default {
			[true] call XfMissionCleanExecuteAll;
		};
	};
};

// This removes all objects, markers, actions and so on.
XfMissionCleanExecuteAll = {
	private ["_action_id"];
	[True] call XfMissionCleanObjects;
	[] call XfMissionCleanExecuteMarkers;
	[] call XfMissionCleanExecuteTriggers;
	[] call XfMissionCleanExecuteTasks;
	If (X_Client) then {
		{
			_action_id = _x select 0;
			player removeAction _action_id;
		} foreach d_mission_actions_list;
		d_mission_actions_list = [];
		d_mission_picked_up_items = [];
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionCleanExecuteAll C","Cleaned up Actions and Picked Up items"];};
	};
	If (X_Server) then {
		d_mission_actions_list = [];
		d_mission_picked_up_items = [];
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionCleanExecuteAll S","Cleaned up Actions and Picked Up items"];};
	};
};

XfMissionCleanExecuteObjects = {
	private ["_force","_i","_count","_JIPH_MissionObjects","_obj","_acv","_params","_grp","_isVehicle"];
	_force = false;
	if (count _this > 0) then {_force = _this select 0};
	If (X_Server) then {
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: Mission B: %4",diag_tickTime,"mission_functions.sqf","XfMissionCleanExecuteObjects",(count d_mission_object_list)];};
		_count = count d_mission_object_list;
		If (_count > 0) then {
			_JIPH_MissionObjects = ["G_MISSION_OBJECTS",[]] call XNetGetJIP;
			for [{_i = _count - 1},{_i >= 0},{_i = _i - 1}] do {
				try {
					if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: Mission O: %4: %5 (%6)",diag_tickTime,"mission_functions.sqf","XfMissionCleanExecuteObjects",typeof(d_mission_object_list select _i),(d_mission_object_list select _i),(alive (d_mission_object_list select _i))];};
					If(!alive (d_mission_object_list select _i) Or _force) then {
						_obj = d_mission_object_list select _i;
						if (count _JIPH_MissionObjects >0) then {
							_obj_pos = _JIPH_MissionObjects find _obj;
							if (_obj_pos >= 0) then {
								_JIPH_MissionObjects set [_obj_pos,"XdelX"];
								_JIPH_MissionObjects = _JIPH_MissionObjects - ["XdelX"];
							};
						};
						_isVehicle = false;
						if ((_obj isKindOf "LandVehicle") || (_obj isKindOf "LandVehicle") ||(_obj isKindOf "LandVehicle")) then { _isVehicle = true; };
						if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: Clean O: %4",diag_tickTime,"mission_functions.sqf","XfMissionCleanExecuteObjects",[typeof(_obj),(_obj),(alive _obj),_isVehicle,isNull _obj]];};
						If (!isNull _obj) then {
							_acv = _obj getVariable "ACVType";
							if ((!isNil "_acv") and (call XfMissionACVEnabled)) then {
								// Inform ACV of the deletion
								_params = [];
								_params set [ACV_NET_PARAM_CLASS, _obj];
								_params set [ACV_NET_PARAM_OWNER, "Mission"];
								_params set [ACV_NET_PARAM_DISPLAYNAME, (_obj getVariable "VarName")];
								if (_acv == "OBJ") then {
									if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: ACV Obj: %4",diag_tickTime,"mission_functions.sqf","XfMissionCleanExecuteObjects",_params];};
									[_params] call ACV_FNCT_DeleteObject;
								};
								if (_acv == "GRP") then {
									_grp = objNull;
									call compile format ["_grp = %1",(_obj getVariable "VarName")];
									_params set [ACV_NET_PARAM_CLASS, _grp];
									if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: ACV Unit: %4",diag_tickTime,"mission_functions.sqf","XfMissionCleanExecuteObjects",_params];};
									[_params] call ACV_FNCT_DeleteUnit;
								};
								If (_acv != "OBJ" || _acv != "GRP") then {
									if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: Clean O: ACV Type Unknown %4",diag_tickTime,"mission_functions.sqf","XfMissionCleanExecuteObjects",_acv];};
								};
							};
							if (!isNull _obj) then {
								if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: Clean O: %4",diag_tickTime,"mission_functions.sqf","XfMissionCleanExecuteObjects","standard cleanup"];};
								_has_players = false;
								if (_isVehicle) then {
									{	if (isPlayer _x) then { _has_players = true; }
										else { deleteVehicle _x; };
									} foreach crew _obj;
								};
								if (!_has_players) then { deleteVehicle (_obj); };
							};
						};
						// Si on n'a pas pu l'effacer, on le fera la prochaine fois ;-)
						if (isNull _obj) then { d_mission_object_list set [_i,"XdelX"]; };
						d_mission_object_list = d_mission_object_list - ["XdelX"];
					};
				} catch	{
				
				};
			};
			["G_MISSION_OBJECTS",_JIPH_MissionObjects] call XNetSetJIP;
		};
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: Mission A: %4",diag_tickTime,"mission_functions.sqf","XfMissionCleanExecuteObjects",(count d_mission_object_list)];};
		// This will clean dead units created by external scripts like reinforcement script
		If (! isnil "d_allunits_add") then {
			if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: AllUnits B: %4",diag_tickTime,"mission_functions.sqf","XfMissionCleanExecuteObjects",(count d_allunits_add)];};
			_count = count d_allunits_add;
			If (_count > 0) then {
				for [{_i = _count - 1},{_i >= 0},{_i = _i - 1}] do {
					try {
						if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: AllUnits O: %4: %5 (%6)",diag_tickTime,"mission_functions.sqf","XfMissionCleanExecuteObjects",typeof(d_allunits_add select _i),(d_allunits_add select _i),(alive (d_allunits_add select _i))];};
						if (_i < count d_allunits_add) then {
							If(!alive (d_allunits_add select _i) Or _force) then {
								d_allunits_add set [_i,"XdelX"];
								d_allunits_add = d_allunits_add - ["XdelX"];
								_acv = _obj getVariable "ACVType";
								if ((!isNil "_acv") and (call XfMissionACVEnabled)) then {
									// Inform ACV of the deletion
									_params = [];
									_params set [ACV_NET_PARAM_CLASS, _obj];
									_params set [ACV_NET_PARAM_OWNER, "Mission"];
									_params set [ACV_NET_PARAM_DISPLAYNAME, (_obj getVariable "VarName")];
									if (_acv == "OBJ") then {
										if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: ACV Obj: %4",diag_tickTime,"mission_functions.sqf","XfMissionCleanExecuteObjects",_params];};
										[_params] call ACV_FNCT_DeleteObject;
									};
									if (_acv == "GRP") then {
										_grp = objNull;
										call compile format ["_grp = %1",(_obj getVariable "VarName")];
										_params set [ACV_NET_PARAM_CLASS, _grp];
										if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: ACV Unit: %4",diag_tickTime,"mission_functions.sqf","XfMissionCleanExecuteObjects",_params];};
										[_params] call ACV_FNCT_DeleteUnit;
									};
								} else {
									deleteVehicle (d_allunits_add select _i);
								};
							};
						};
					} catch {
					
					};
				};
			};
			if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: AllUnits A: %4",diag_tickTime,"mission_functions.sqf","XfMissionCleanExecuteObjects",(count d_allunits_add)];};
		};
	};
};
XfMissionCleanExecuteMarkers = {
	private ["_force"];
	_force = false;
	if (count _this > 0) then {_force = _this select 0};
	If (X_Client) then {
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: Mission: %4",diag_tickTime,"mission_functions.sqf","XfMissionCleanExecuteMarkers C",(count d_mission_marker_list)];};
		{
			deleteMarkerLocal _x;
		} foreach d_mission_marker_list;
		d_mission_marker_list= [];
	};
	If (X_Server) then {
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: Mission: %4",diag_tickTime,"mission_functions.sqf","XfMissionCleanExecuteMarkers S",(count d_mission_marker_list)];};
		{
			deleteMarkerLocal _x;
		} foreach d_mission_marker_list;
		d_mission_marker_list= [];
		["current_mission_markers",[]] call XNetSetJIP;
	};
};
XfMissionCleanExecuteTriggers = {
	private ["_force"];
	_force = false;
	if (count _this > 0) then {_force = _this select 0};
	If (X_Client) then {
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: Mission: %4",diag_tickTime,"mission_functions.sqf","XfMissionCleanExecuteTriggers C",(count d_mission_trigger_list)];};
		{
			deleteVehicle _x;
		} foreach d_mission_trigger_list;
		d_mission_trigger_list = [];
	};
	If (X_Server) then {
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: Mission: %4",diag_tickTime,"mission_functions.sqf","XfMissionCleanExecuteTriggers S",(count d_mission_trigger_list)];};
		{
			deleteVehicle _x;
		} foreach d_mission_trigger_list;
		d_mission_trigger_list = [];
	};
};

XfMissionCleanExecuteTasks = {
	private ["_force"];
	_force = false;
	if (count _this > 0) then {_force = _this select 0};
	If (X_Client) then {
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: Mission: %4",diag_tickTime,"mission_functions.sqf","XfMissionCleanExecuteTasks C",count(player getVariable "mission_created_tasks")];};
		{
			player removeSimpleTask (_x select 1);
		} foreach (player getVariable "mission_created_tasks");
		player setVariable ["mission_created_tasks",[]];
	};
	If (X_Server) then {
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: Mission: %4",diag_tickTime,"mission_functions.sqf","XfMissionCleanExecuteTasks S",count(["current_mission_tasks",[]] call XNetGetJIP)];};
		["current_mission_tasks",[]] call XNetSetJIP;
	};
};

XfMissionCleanAll = { ["d_all_mclean",["ALL",_this]] call XNetCallEvent;  };
XfMissionCleanObjects = { ["d_all_mclean",["OBJECTS",_this]] call XNetCallEvent;  };
XfMissionCleanMarkers = { ["d_all_mclean",["MARKERS",_this]] call XNetCallEvent;  };
XfMissionCleanTriggers = { ["d_all_mclean",["TRIGGERS",_this]] call XNetCallEvent;  };
XfMissionCleanTasks = { ["d_all_mclean",["TASKS",_this]] call XNetCallEvent;  };

XfMissionCloseMission = {
	["current_mission_position",[[0,0,0],0,""]] call XNetSetJIP;
	["current_mission_id",0] call XNetSetJIP; 
	["current_mission_completed",true] call XNetSetJIP; 
	If (!isNil "DM_Mission_Name") then {
		if (DM_Mission_Name != "") then {
			// We inform all systems
			["d_all_exec",[Format["titleText [""Ending Mission\n\n~ %1 ~"",""BLACK""];titleFadeOut 5;",DM_Mission_Name]]] call XNetCallEvent;
		};
	};
	// We set global variables from other scripts which could have been changed by the mission.
	KRON_UPS_sharedist = DM_Mission_UPSMON_Share_Dist;
	// We reset all variables to prevent porting them to the next mission
	DM_Mission_AirSupport = 0;
	DM_Mission_EnemyPower = 0;
	DM_Mission_EnemyCoeff = 0;
	DM_Mission_Chapter_Success = 0; // 0 is pending, 1 = success, -1 = failure. You can use other values in chapters for multi-branching.
	DM_Mission_Mission_CanRun = True; // Setting it to false will exit the chapters and mission execution. This allows for interrupting the mission.
	DM_Mission_Mission_Runs = False; // This will be set to false when the mission is stopped
	DM_Mission_Chapter_CanRun = True; // Setting it to false will exit the chapters and mission execution. This allows for interrupting the mission.
	DM_Mission_Chapter_Runs = False; // The current status of the running chapter
	DM_Mission_RandomReinforcement = 0; // When a mission is running, this will send reinforcement from time to time if the mission current position is defined.
	DM_Mission_Snipers = 0; // When creating a chapter, this will create snipers on the mission position randomly.
	DM_Mission_Custom_Groups = [];
};

/// @FUNCTION
/// @NAME XfMissionBuildingAreaPos
/// @DESC Returns an array with all the available building pos of the given type in the given area.
/// @ACCESS Server
/// @MP False
/// @PARAM Position Center of the area to search
/// @PARAM Number The size of the area to search into @DEFAULT 100
/// @PARAM Variant The type of position to search: INTERN or 0, COMBAT or 1, HIDE or 2, SNIPER or 3 @DEFAULT "INTERN"
/// @PARAM Boolean Whether building pos returned by the internal building pos should be added too. @DEFAULT False
/// @RETURN Array A list of possible positions of all buildings
/// @EXAMPLE [[5892.23,10198.2,0],200] call XfMissionBuildingAreaPos;
/// @EXAMPLE [[5892.23,10198.2,0],200,"HIDE"] call XfMissionBuildingAreaPos;
XfMissionBuildingAreaPos = {
	private ["_pos","_array","_size","_type","_force_buildingPos","_arr"];
	_pos = _this select 0;
	_size = 100;
	_type = "INTERN";
	_force_buildingPos = False;
	if (count _this > 1) then {_size = _this select 1};
	if (count _this > 2) then {_type = _this select 2};
	if (count _this > 3) then {_force_buildingpos = _this select 3};
	if (d_mission_debug_messages) then { 
		diag_log "";
		diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionBuildingAreaPos",_this];
	};		_array = [];
	_lst = nearestObjects [_pos, ["Building"], _size];
	If (!isNil "_lst") then {
		{
			_arr = [_x,_type,_force_buildingPos] call XfMissionBuildingPos;
			if (!isNil "_arr") then {
				{ _array set [count _array, _x];
				}foreach _arr;
			};
		} foreach _lst;
	};
	// We shuffle the array for diversity
	if (count _array > 0) then { _array = _array call XfRandomArray; };
	_array;
};

/// @FUNCTION
/// @NAME XfMissionBuildingAreaPosASL
/// @DESC Returns an array with all the available building pos of the given type in the given area. Those are ABOVE SEAS LEVEL positions. Use SetPosASL accordingly!
/// @ACCESS Server
/// @MP False
/// @PARAM Position Center of the area to search
/// @PARAM Number The size of the area to search into @DEFAULT 100
/// @PARAM Variant The type of position to search: INTERN or 0, COMBAT or 1, HIDE or 2, SNIPER or 3 @DEFAULT "INTERN"
/// @PARAM Boolean Whether building pos returned by the internal building pos should be added too. @DEFAULT False
/// @RETURN Array A list of possible positions of all buildings
/// @REMARK <font color="RED">ATTENTION</font> This function returns ASL positions, use setPosASL. Otherwise use the regular version.
/// @EXAMPLE [[5892.23,10198.2,0],200] call XfMissionBuildingAreaPosASL;
/// @EXAMPLE [[5892.23,10198.2,0],200,"SNIPER"] call XfMissionBuildingAreaPosASL;
XfMissionBuildingAreaPosASL = {
	private ["_pos","_array","_size","_type","_force_buildingPos","_arr","_currsize"];
	_pos = _this select 0;
	_size = 100;
	_type = "INTERN";
	_force_buildingPos = False;
	if ((count _pos) < 3) exitWith {diag_log Format["(%1)[%2]: %3: %5: %4",diag_tickTime,"mission_functions.sqf","XfMissionBuildingAreaPosASL","Invalid parameters",_this];};
	if (count _this > 1) then {_size = _this select 1};
	if (count _this > 2) then {_type = _this select 2};
	if (count _this > 3) then {_force_buildingpos = _this select 3};
	// if (d_mission_debug_messages) then { 
		// diag_log "";
		// diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionBuildingAreaPosASL",_this];
	// };		
	_array = [];
	_lst = [];
	// Increases the size automagically, in order to find a nearby match
	_currsize = _size;
	while { ((count _lst <= 0) && _size >= 150 && _currsize <= _size) || ((count _lst <= 0) && _currsize < 150)} do {
		_lst = nearestObjects [_pos, ["Building"], _currsize];
		_currsize = _currsize + 20;
	};
	// if (d_mission_debug_messages) then { 
		// diag_log Format["(%1)[%2]: %3: list:%4",diag_tickTime,"mission_functions.sqf","XfMissionBuildingAreaPosASL",_lst];
	// };
	If ((count _lst) > 0) then {
		{
			_arr = [_x,_type,_force_buildingPos] call XfMissionBuildingPosASL;
			// diag_log Format["(%1)[%2]: %3: adding:%4",diag_tickTime,"mission_functions.sqf","XfMissionBuildingAreaPosASL",_arr];
			if (!isNil "_arr") then {
				{ _array set [count _array, _x];
				}foreach _arr;
			};
		} foreach _lst;
	};
	// We shuffle the array for diversity
	if (count _array > 0) then { _array = _array call XfRandomArray; };
	// if (d_mission_debug_messages) then { 
		// diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionBuildingAreaPosASL",_array];
		// diag_log "";
	// };		

	_array;
};

/// @FUNCTION
/// @NAME XfMissionBuildingPos
/// @DESC Returns an array with all the available building pos of the given type in the given building
/// @ACCESS Server
/// @MP False
/// @PARAM Object The building to search
/// @PARAM Variant The type of position to search: INTERN or 0, COMBAT or 1, HIDE or 2, SNIPER or 3 @DEFAULT "INTERN"
/// @PARAM Boolean Whether building pos returned by the internal building pos should be added too. @DEFAULT False
/// @RETURN Array A list of possible positions of all buildings
/// @EXAMPLE [_house,"HIDE"] call XfMissionBuildingPos; // Will return all house positions where a target AI can hide.
/// @EXAMPLE [_house,"COMBAT"] call XfMissionBuildingPos; // Will return all house positions where AI soldiers can hide and fire on passing players
/// @EXAMPLE [_house,"SNIPER"] call XfMissionBuildingPos; // Will return all sniper positions
XfMissionBuildingPos = {
	private ["_bld","_array","_current_index","_current_pos","_force_buildingPos","_type"];
	_type = "INTERN";
	_force_buildingPos = False;
	_use_buildingPos = True;
	If (typeName _this == "ARRAY") then {
		_bld = _this select 0;
		if (count _this > 1) then {_type = _this select 1};
		if (count _this > 2) then {_force_buildingpos = _this select 2};
	} else {
		_bld = _this;
	};
	if (_type == "HIDE" || _type == "SNIPER") then {
		_use_buildingPos = False;
	};
	_array = [];
	if (d_mission_debug_messages) then { 
		diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionBuildingPos",[_bld,_type,_use_buildingPos, _force_buildingPos]];
	};		
	if (!isNil "XfBuildingPosArray") then {
		// Using homebrewn building pos
		_array = [_bld,_type,_use_buildingPos, _force_buildingPos] call XfBuildingPosArray;
	} else {
		_current_index = 0;
		_current_pos = [0,0,0];
		while {_current_index < 50} do
		{
			_current_pos = _bld buildingPos _current_index;
			// If the returned position is 0,0,0 it means there are no more positions
			if (abs(_current_pos distance [0,0,0]) <= 0.01) exitWith {};
			_array set [count _array, _current_pos];
			_current_index = _current_index + 1;
		};
	};
	_array;
};

/// @FUNCTION
/// @NAME XfMissionBuildingPosASL
/// @DESC Returns an array with all the available building pos of the given type in the given building but Above Sea Level, thus you need to use SetPosASL.
/// This is the best method when you need to deal with positions above the roof like sniper positions.
/// @ACCESS Server
/// @MP False
/// @PARAM Object The building to search
/// @PARAM Variant The type of position to search: INTERN or 0, COMBAT or 1, HIDE or 2, SNIPER or 3 @DEFAULT "INTERN"
/// @RETURN Array A list of possible positions of all buildings
/// @EXAMPLE [_house,"HIDE"] call XfMissionBuildingPosASL; // Will return all house positions where a target AI can hide.
/// @EXAMPLE [_house,"COMBAT"] call XfMissionBuildingPosASL; // Will return all house positions where AI soldiers can hide and fire on passing players
/// @EXAMPLE [_house,"SNIPER"] call XfMissionBuildingPosASL; // Will return all sniper positions
XfMissionBuildingPosASL = {
	private ["_bld","_array","_current_index","_current_pos","_force_buildingPos","_type"];
	_type = "INTERN";
	If (typeName _this == "ARRAY") then {
		_bld = _this select 0;
		if (count _this > 1) then {_type = _this select 1};
	} else {
		_bld = _this;
	};
	// if (d_mission_debug_messages) then { 
		// diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionBuildingPosASL",[_bld,_type,false, false]];
	// };		
	// We never accept building positions returned by BuildingPos, because they are the evil!... and not compatible with ASL :)
	_array = [_bld,_type,false, false] call XfBuildingPosArrayASL;
	_array;
};

/// @FUNCTION
/// @NAME XfMissionRanObjectivePos
/// @DESC Returns an array with all the available building pos of the given type in the given building but Above Sea Level, thus you need to use SetPosASL.
/// This is the best method when you need to deal with positions above the roof like sniper positions.
/// @ACCESS Server
/// @MP False
/// @PARAM Position The center position
/// @PARAM Scalar The placement radius
/// @PARAM Variant The class of the object to place as a string, or the size we want to check. Bigger than 20 or smaller. The class name doesn't seem reliable for static objects.
/// @PARAM Position Default position if none found. @DEFAULT = []
/// @RETURN Position Where to place the object
/// @EXAMPLE _obj_pos = [_position,_radius,"class"] call XfMissionRanObjectivePos; // Will return all house positions where a target AI can hide.
XfMissionRanObjectivePos = {
	private ["_center","_radius","_class","_default","_currpos","_return","_try","_size"];
	_center = _this select 0;
	_radius = _this select 1;
	_class = _this select 2;
	_default = [];
	_return = [];
	if (count _this > 3) then { _default = _this select 3; };
	_try = 40;
	while { _try > 0 && (count _return <= 0)} do {
		_currpos = [];
		if (typeName _class == "STRING") then {
			_size = (sizeOf _class);
			if (_size <= 0) then {_size = 20;};
			_currpos = [_center, _radius,_size] call XfGetRanPointCircleBig;
			if (count _currpos > 0) then { _currpos = _currpos findEmptyPosition [1,50,_class]; };
		} else {
			_currpos = [_center, _radius, _class] call XfGetRanPointCircleBig;
			if (count _currpos > 0) then { _currpos = _currpos findEmptyPosition [1,50]; };
		};
		if (count _currpos == 3) then {
			// diag_log Format["(%1)[%2]: %3: type:%4",diag_tickTime,"mission_functions.sqf","XfMissionRanObjectivePos",surfaceType [(_currpos select 0), (_currpos select 1)]];
			_return = _currpos;
		};
		_try = _try - 1;
	};
	if (count _return <= 0) then {_return = _default;};
	_return;
};

/// @FUNCTION
/// @NAME XfMissionCreateMissionObject
/// @DESC 
/// @ACCESS Server
/// @MP False
/// @PARAM String The type of the object to create or an existing object
/// @PARAM Position The position where the object should be created. Can be a position or a combination of pos and dir: [[pos],dir]
/// @PARAM String The Object action type: REGISTER, DESTROY, ASSASSINATE, etc @DEFAULT "REGISTER"
/// @PARAM String The action parameters @DEFAULT []
/// @PARAM String The init line of the object @DEFAULT = ""
/// @PARAM String The special creation parameter: NONE, CAN_COLLIDE, etc. @DEFAULT = "CAN_COLLIDE"
/// @RETURN Object The created object
/// @REMARK Will sync with the ACV if it's present.
/// @EXAMPLE _obj = ["EvMoney",getPos "Marker1","PICKUP",["Olaf's Money","You found the money","jip_money_found","Take the money",True]] call XfMissionCreateMissionObject;
XfMissionCreateMissionObject = {
	private ["_obj_type","_obj_pos","_obj_dir","_obj_action","_obj_initline","_obj_special","_obj","_obj_name","_acv_params"];
	_obj_type = _this select 0;
	_obj_pos = _this select 1;
	// Case where we receive the position plus the direction.
	if (typename (_obj_pos select 0) == "ARRAY") then {
		_obj_dir = _obj_pos select 1;
		_obj_pos = _obj_pos select 0;
	};	
	_obj_action = "REGISTER";
	If (count _this > 2) then {_obj_action = _this select 2;};
	_obj_params = [];
	If (count _this > 3) then {_obj_params = _this select 3;};
	_obj_initline = "";
	If (count _this > 4) then {_obj_initline = _this select 4;};
	_obj_special = "CAN_COLLIDE";
	If (count _this > 5) then {_obj_special = _this select 5;};
	_obj = objNull;	
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: type:%4  pos:%5  act:%6   pars:%7   init:%8  special:%9  acv:%10",diag_tickTime,"mission_functions.sqf","XfMissionCreateMissionObject",_obj_type,_obj_pos,_obj_action,_obj_params,_obj_initline,_obj_special,(call XfMissionACVEnabled)];};

	// Object position correction
	if ((_obj_pos select 2) < 0) then {_obj_pos set [2,0]};

	// Checks if the object already exists
	If (typeName _obj_type == "OBJECT") then {
		_obj = _obj_type;
		_obj_type = typeOf _obj;
	} else {
		if (call XfMissionACVEnabled) then {
			// Creating using the ACV
			if (isNil "_obj_dir") then {_obj_dir = 0;};
			_obj_name = _obj_type;
			if (typename (_obj_params) == "ARRAY") then {
				if (count _obj_params > 0) then {
					if ((typename (_obj_params select 0) == "STRING") AND (_obj_action == "PICKUP")) then {_obj_name = _obj_params select 0;};
				};
			};
			_acv_params = [];
			_acv_params set [ACV_NET_PARAM_TYPE, ACV_NET_MAKESIMPLEOBJECT];
			_acv_params set [ACV_NET_PARAM_CLASS, _obj_type];
			_acv_params set [ACV_NET_PARAM_POS, _obj_pos];
			_acv_params set [ACV_NET_PARAM_DIR, _obj_dir];
			_acv_params set [ACV_NET_PARAM_DISPLAYNAME, _obj_name];
			_acv_params set [ACV_NET_PARAM_OWNER, "Mission"];
			_acv_params set [ACV_NET_PARAM_SIDE, objNull];
			_acv_params set [ACV_NET_PARAM_SKILL, objNull];
			_acv_params set [ACV_NET_PARAM_RANK, objNull];
			_acv_params set [ACV_NET_PARAM_FLYING, objNull];
			_acv_params set [ACV_NET_PARAM_OTHER, ""];
			_acv_params set [ACV_NET_PARAM_SPECIAL, _obj_special];
			_obj = [_acv_params] call ACV_FNCT_MakeObjects;
		} else {
			// Creating using the mission framework
			_obj = createVehicle [_obj_type, _obj_pos, [], 0, _obj_special];
			_obj setPos _obj_pos;
			if (!isNil "_obj_dir") then {_obj setDir _obj_dir;}; 
		};
	};
	if (_obj_initline != "") then {
		_obj setVehicleInit _obj_initline;
		processInitCommands;
	};
	// We set the action and parameters for the registration
	[_obj, _obj_action] call XfMissionSetAction;
	[_obj, _obj_params] call XfMissionSetParameters;
	// We add the object to the list of things which need to be synchronized in JIP
	_obj call XfMissionAddObject;
	// We register the object on all systems
	["d_all_mobject",_obj] call XNetCallEvent;
	// We return the object.
	_obj;
};

/// @FUNCTION
/// @NAME XfMissionCreateMissionUnit
/// @DESC 
/// @ACCESS Server
/// @MP False
/// @PARAM String Name of the unit type
/// @PARAM Variant A side string or a group object
/// @PARAM Position Either a position or a combined position and direction [[pos],dir]
/// @PARAM String The action type of the object: REGISTER, ASSASSINATE, DESTROY, etc. @DEFAULT "REGISTER"
/// @PARAM Array The actions parameters for the given action. @DEFAULT []
/// @PARAM String The unit init line @DEFAULT ""
/// @PARAM String The unit special creation parameter (NONE, FORM, FLY, etc.) @DEFAULT "NONE"
/// @RETURN Object The created object
/// @REQUIRE Domination Template
/// @REMARK 
/// @EXAMPLE ["RU_Profiteer2",_grp,[5892.23,10198.2,0],"ASSASSINATE",["You killed the traitor","jip_traitor_killed"],"doStop this;"] call XfMissionCreateMissionUnit;
XfMissionCreateMissionUnit = {
	private ["_obj_type","_obj_group","_obj_pos","_obj_dir","_obj_action","_obj_initline","_obj_special","_obj","_crew","_acv_params","_grp"];
	_obj_type = _this select 0;
	_obj_group = _this select 1;
	_obj_pos = _this select 2;
	// Case where we receive the position plus the direction.
	if (typename (_obj_pos select 0) == "ARRAY") then {
		_obj_dir = _obj_pos select 1;
		_obj_pos = _obj_pos select 0;
	};	
	_obj_action = "REGISTER";
	If (count _this > 3) then {_obj_action = _this select 3;};
	_obj_action = ToUpper(_obj_action);
	_obj_params = [];
	If (count _this > 4) then {_obj_params = _this select 4;};
	_obj_initline = "";
	If (count _this > 5) then {_obj_initline = _this select 5;};
	_obj_special = "NONE";
	If (count _this > 6) then {_obj_special = _this select 6;};
	_obj = objNull;
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: type:%4  grp:%5  pos:%6  act:%7   pars:%8   init:%9  special:%10  isman:%11",diag_tickTime,"mission_functions.sqf","XfMissionCreateMissionUnit",_obj_type,_obj_group,_obj_pos,_obj_action,_obj_params,_obj_initline,_obj_special,(_obj_type isKindOf "Man")];};
	
	// Object position correction
	if ((_obj_pos select 2) < 0) then {_obj_pos set [2,0]};
	
	// Check if the object already exists
	If (typeName _obj_type == "OBJECT") then {
		_obj = _obj_type;
		_obj_type = typeOf _obj;
	} else {
		// Creating using the mission framework
		// Convert from Side string to a new group
		If (typeName _obj_group == "STRING") then {
			_obj_group = switch (_obj_group) do {
				case "EAST": {_obj_group};
				case "WEST": {_obj_group};
				case "GUER": {_obj_group};
				case "CIV": {_obj_group};
				default {d_enemy_side};
			};
			_obj_group = [_obj_group] call x_creategroup;
		};
		if (_obj_type isKindOf "Man") then {
			_obj = [_obj_type, _obj_group, _obj_pos, 0] call XfCreateUnit;
			// _obj = _obj_group createUnit [_obj_type, _obj_pos, [], 0, _obj_special];
			if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: grp:%4  type:%5  obj:%6",diag_tickTime,"mission_functions.sqf","XfMissionCreateMissionUnit",group _obj, typeof _obj, _obj];};
			_obj setVariable ["BIS_noCoreConversations", true];
		} else {
			_obj = createVehicle [_obj_type, _obj_pos, [], 0, _obj_special];
			_crew = [_obj, _obj_group] call XfSpawnCrew;
		};
		_obj setPos _obj_pos;
		if (!isNil "_obj_dir") then {_obj setDir _obj_dir;};
		// Registering in the ACV if necessary
		if (call XfMissionACVEnabled) then {
			_obj_name = _obj_type;
			if (typename (_obj_params) == "ARRAY") then {
				if (count _obj_params > 0) then {
					if ((typename (_obj_params select 0) == "STRING") AND (_obj_action == "PICKUP")) then {_obj_name = _obj_params select 0;};
				};
			};
			_type = 0;
			if (_obj_type isKindOf "Man") then {
				_type = ACV_NET_MAKEUNITS;
			} else {
				_type = ACV_NET_MAKEVEHICLES;
			};
			[_type, _obj_group, _obj_name, "MissionFramework"] call ACV_FNCT_PutInStock;
			[ format[localize "STR_MSG_CREATEDBY",_obj_name, "MissionFramework"]] call ACV_FNCT_BroadcastMessage;
		};
		// if (call XfMissionACVEnabled) then {
			// // Creating using the ACV
			// if (isNil "_obj_dir") then {_obj_dir = random 360;};
			// _obj_name = _obj_type;
			// if (typename (_obj_params) == "ARRAY") then {
				// if (count _obj_params > 0) then {
					// if ((typename (_obj_params select 0) == "STRING") AND (_obj_action == "PICKUP")) then {_obj_name = _obj_params select 0;};
				// };
			// };
			// _obj_flying = 0;
			// If (_obj_special == "FLY") then { _obj_flying = 1; };
			// _acv_params = [];
			// _acv_params set [ACV_NET_PARAM_TYPE, 0];
			// _acv_params set [ACV_NET_PARAM_CLASS, _obj_type];
			// _acv_params set [ACV_NET_PARAM_POS, _obj_pos];
			// _acv_params set [ACV_NET_PARAM_DIR, _obj_dir];
			// _acv_params set [ACV_NET_PARAM_DISPLAYNAME, _obj_name];
			// _acv_params set [ACV_NET_PARAM_OWNER, "Mission"];
			// _acv_params set [ACV_NET_PARAM_SIDE, _obj_group];
			// _acv_params set [ACV_NET_PARAM_SKILL, (call XfGetUnitSkill)];
			// _acv_params set [ACV_NET_PARAM_RANK, "corporal"];
			// _acv_params set [ACV_NET_PARAM_FLYING, _obj_flying];
			// _acv_params set [ACV_NET_PARAM_OTHER, ""];
			// _acv_params set [ACV_NET_PARAM_SPECIAL, _obj_special];
			// if (_obj_type isKindOf "Man") then {
				// _acv_params set [ACV_NET_PARAM_TYPE, ACV_NET_MAKEUNITS];
				// _grp = [_acv_params] call ACV_FNCT_MakeUnits;
				// _obj = leader _grp;
				// _obj setPos _obj_pos;
			// } else {
				// _acv_params set [ACV_NET_PARAM_TYPE, ACV_NET_MAKEVEHICLES];
				// _grp = [_acv_params] call ACV_FNCT_MakeVehicles;
				// _obj = vehicle (leader _grp);
			// }
		// } else {
			// // Creating using the mission framework
			// // Convert from Side string to a new group
			// If (typeName _obj_group == "STRING") then {
				// _obj_group = switch (_obj_group) do {
					// case "EAST": {_obj_group};
					// case "WEST": {_obj_group};
					// case "GUER": {_obj_group};
					// case "CIV": {_obj_group};
					// default {d_enemy_side};
				// };
				// _obj_group = [_obj_group] call x_creategroup;
			// };
			// if (_obj_type isKindOf "Man") then {
				// _obj = _obj_group createUnit [_obj_type, _obj_pos, [], 0, _obj_special];
			// } else {
				// _obj = createVehicle [_obj_type, _obj_pos, [], 0, _obj_special];
				// _crew = [_obj, _obj_group] call XfSpawnCrew;
			// };
			// _obj setPos _obj_pos;
			// if (!isNil "_obj_dir") then {_obj setDir _obj_dir;}; 
		// };
	};

	// We set the action and parameters for the registration
	[_obj, _obj_action] call XfMissionSetAction;
	[_obj, _obj_params] call XfMissionSetParameters;
	// We add the object to the list of things which need to be synchronized in JIP
	_obj call XfMissionAddObject;
	if (_obj_initline != "") then {
		_obj setVehicleInit _obj_initline;
		processInitCommands;
	};
	// We register the object on all systems
	["d_all_mobject",_obj] call XNetCallEvent;
	// We return the object.
	_obj;
};

// // [[["East","RU","Infantry","RU_InfSquad"],_pos,_dir,_skill,_init]] call XfMissionCreateGroup;
// // [[["East"],_pos]] call XfMissionCreateGroup; -- Random east side group
// // [[["East","RU"],_pos]] call XfMissionCreateGroup; -- Random RU side group
// // [[["East","RU","Infantry"],_pos,_dir,_skill]] call XfMissionCreateGroup; -- Random RU Infantry group
// // [[["East","Infantry"],_pos,_dir,_skill,_init]] call XfMissionCreateGroup; -- Random East Infantry group
// // [[["East","RU_InfSquad"],_pos]] call XfMissionCreateGroup; -- The given groupe name
// // [[["CustomGroup"],_pos,_dir,_skill]] call XfMissionCreateGroup; -- The name of a custom group defined in a mission
// XfMissionCreateGroup = {
	// private ["_params","_type","_pos","_dir","_displayname","_owner","_side","_str_side","_skill","_arr","_group","_x","_arr_wp"];
	// try {
		// _params = _this select 0;
		// _type = _params select 0;
		// _pos = _params select 1;
		// _dir = random 360;
		// if (count _params > 2) then { _dir = _params select 2; };
		// _skill = call XfGetUnitSkill;
		// if (count _params > 3) then { _skill = _params select 3; };
		// _init = ""
		// if (count _params > 4) then { _init = _params select 4; };
		
		// _side = d_enemy_side;
		// _faction = "";
		// _group_type = "";
		// _class_name = "";
		// _custom_group = "";
		
		// // The minimum info is the side or the custom group
		// _side = switch (toUpper(_type select 0)) do {
			// case "EAST": {"EAST"};
			// case "WEST": {"WEST"};
			// case "GUER": {"GUER"};
			// case "CIV": {"CIV"};
			// default {""};
		// };
		// // If the side is not a side, it's probably a custom group.
		// // If not, we quit.
		// {
			// if (toUpper(_x select 0) == toUpper(_side)) exitWith { _custom_group = _x select 0 };
		// } foreach DM_Mission_Custom_Groups;
		// if (_side == "" && _custom_group == "") exitWith {diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","Invalid side or custom group",_type];};

		// // It is a side and not a custom group
		// If (_side != "") then {
			// // We shorten the group side list.
			// _param2 = "";
			// _param3 = "";
			// _param4 = "";
			
			// _groups_side = [];
			// {
				// _b_valid = true;
				// _groups_side set [count _groups_side,_x];
			// } foreach MISSION_ARRAY_GROUP_NAME;
			
			
			// {
				// if ((_x select 0) == _side) exitWith {_faction = _x select 2;}
			// } foreach MISSION_ARRAY_GROUP_NAME;
			// if (count _type > 1) then {
				// {
					// if (((_x select 0) == _side) && ((_x select 2) == _faction)) exitWith {_faction = _x select 2;}
				// } foreach MISSION_ARRAY_GROUP_NAME;
			// };
		// } else {
			
		// };
		
		// _side = _type select 0;
			// _arr = [];
	// _arr set [0, _txt_side];
	// _arr set [1, ACV_Sel_Faction];
	// _arr set [2, _type_group];
	// _arr set [3, _classname];
		// if (d_mission_debug_messages) then { 
			// diag_log "";
			// diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionCreateGroup",_this];
		// };
		
		// If (typeName _obj_group == "STRING") then {
			// _obj_group = switch (_obj_group) do {
				// case "EAST": {_obj_group};
				// case "WEST": {_obj_group};
				// case "GUER": {_obj_group};
				// case "CIV": {_obj_group};
				// default {d_enemy_side};
			// };
			// _obj_group = [_obj_group] call x_creategroup;
		// };

		// diag_log format["*** ACV MAKE GROUP *** %1",_params];
		// if (_displayname != "") then {
			// _side = if (_str_side == "Guerrila") then {resistance;} else {call compile format ["%1",_str_side];};
			// _group = [_pos,_side,(configFile >> "CfgGroups" >> (_arr select 0) >> (_arr select 1) >> (_arr select 2) >> (_arr select 3)),[],[],[],[],[],_dir] call BIS_fnc_spawnGroup;
			// _group setFormDir _dir;
			// {	_x setskill _skill;
			// } foreach units _group;
			// _arr_wp = waypoints _group;
			// (_arr_wp select 0) setWPPos _pos;
			// [_type, _group, _displayname, _owner] call ACV_FNCT_PutInStock;
			// [format[localize "STR_MSG_CREATEDBY",_displayname,_owner]] call ACV_FNCT_BroadcastMessage;

		// } else {
			// [format[localize "STR_MSG_GROUPNOTCREATED",_owner]] call ACV_FNCT_BroadcastMessage;
		// };
	// } catch {
		// _group = grpNull;
	// };
	// _group;
// };

// Create a group of units
// Parameters:
//		- group Type: Can be a single value like ["RU_InfSquad"] or multiple values detailing each unit, they'll be added to the same group. It must be an array!
//		- isInfantry : either True if the unit is footmen or false if it's a vehicle.
//		- group side: Used to create the necessary group
//		- group position: Starting position of the group.
//		- UPSMON Parameters (leave as empty array if it doesn't need to be added to UPSMON)
//		- (Optional) TaskPatrol: True, uses TaskPatrol script, False to skip it. Default= TRUE, only activated if UPSMON params are empty! If TaskPatrol is skipped, then TaskDefend will be used.
//		- (Optional) leader initline, default = ""
//		- (Optional) object special creation parameter, default = "FORM" 
// Ex:	Footmen UPSMON: ["RU_InfSquad",true,"EAST",[[centerpos],radius],["markername","random","noveh","aware","buldings"]] call XfMissionCreateUnitGroup;
// 		Vehicle UPSMON: ["ZSU_INS",false,"EAST",[[centerpos],radius],["markername","random","onroad"]] call XfMissionCreateUnitGroup;
// 		Footmen TaskDefend: ["RU_InfSquad",true,"EAST",[[centerpos],radius],[]] call XfMissionCreateUnitGroup;
// 		Footmen No TaskDefend: ["RU_InfSquad",true,"EAST",[[centerpos],radius],[],False] call XfMissionCreateUnitGroup;
XfMissionCreateUnitGroup = {
	private ["_group_types","_group_side","_group_position","_upsmon","_taskpatrol","_leader_init","_formation","_pos","_radius","_pos_center","_units","_newgroup","_leader","_vecs","_vehicles","_i","_debug"];
	_group_types = _this select 0;
	_is_infantry = _this select 1;
	_group_side = _this select 2;
	_group_position = _this select 3;
	_upsmon = [];

	If (count _this > 4) then {	_upsmon = _this select 4; };
	_taskpatrol = true;
	_debug = false; //= d_mission_debug_messages;
	_radius = 100;
	if (count _this > 5) then {	_taskpatrol = _this select 5; };
	_leader_init = "";
	if (count _this > 6) then {_leader_init = _this select 6; };
	_formation = "FORM";
	if (count _this > 7) then {_formation = _this select 7; };
	_taskdefend = true;
	if (count _this > 8) then {	_taskdefend = _this select 8; };
	if (d_mission_debug_messages) then { 
		diag_log "";
		diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionCreateUnitGroup",_this];
	};	
	if (typeName _group_types == "STRING") then {
		_group_types = [_group_types];
	};
	_obj_name = _group_types select 0;
	_pos_center = _group_position;
	if (typename (_group_position select 0) == "ARRAY") then {
		_radius = _group_position select 1;
		_pos_center = _group_position select 0;
	};	
	if ((count _pos_center) < 3) exitWith {diag_log Format["(%1)[%2]: %3: %4: %5",diag_tickTime,"mission_functions.sqf","XfMissionCreateUnitGroup","Invalid parameters",_this];};
	if (_radius > 0) then {
		// By using this function, we ensure vehicles and statics have enough place.
		_pos = [_pos_center, _radius, 10] call XfGetRanPointCircleBig;
		if (count _pos == 0) then {_pos = [_pos_center, _radius] call XfGetRanPointCircle;};
		_try = 50;
		while {count _pos == 0 && _try > 0} do {_pos = [_pos_center, _radius] call XfGetRanPointCircle; _try = _try -1;};
		if (count _pos == 0) then { _pos = _pos_center; };
	} else {
		_pos = _pos_center;
	};
	if (typeName _group_side != "STRING") then {
		_newgroup = _group_side;
		_group_side = switch (side _group_side) do {
			case west: {"WEST"};
			case east: {"EAST"};
			case resistance: {"GUER"};
			default {"CIV"};
		};
	} else {
		_newgroup = [_group_side] call x_creategroup;	
	};
	_units = [];
	if (_is_infantry) then {
		if (typename _group_types == "STRING") then {_group_types = [_group_types];};
		_units = [_pos, _group_types, _newgroup] call x_makemgroup;
	} else {
		if (typename _group_types == "STRING") then {_group_types = [_group_types];};
		{
			_vecs = [1,_pos, _x, _newgroup,_radius,0] call x_makevgroup;
			_units = [];
			// We only want the vehicle(s).
			_vehicles = [];
			// The _vecs array is of the form: [[vec1, vec2, vec3], [all crews]]
			_vecs = _vecs select 0;
			for [{_i = 0},{_i < (count _vecs)},{_i = _i + 1}] do {
				//if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3:  Veh:%4 ",diag_tickTime,"mission_functions.sqf","XfMissionCreateUnitGroup",_vecs select _i];};
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3:  Veh:%4  Type:%5  Crew:%6",diag_tickTime,"mission_functions.sqf","XfMissionCreateUnitGroup",_vecs select _i,typeOf(_vecs select _i),crew(_vecs select _i)];};
				_vehicles set [count _vehicles, _vecs select _i];
			};
			_units = [_units, _vehicles] call XfArrayPushStack;
		} foreach _group_types;
	};
	if (call XfMissionACVEnabled) then {
		_type = 0;
		if (typename (_obj_params) == "ARRAY") then {
			if (count _obj_params > 0) then {
				if ((typename (_obj_params select 0) == "STRING") AND (_obj_action == "PICKUP")) then {_obj_name = _obj_params select 0;};
			};
		};
		if (_is_infantry) then {
			_type = ACV_NET_MAKEUNITS;
		} else {
			_type = ACV_NET_MAKEVEHICLES;
		};
		[_type, _newgroup, _obj_name, "MissionFramework"] call ACV_FNCT_PutInStock;
		[ format[localize "STR_MSG_CREATEDBY",_obj_name, "MissionFramework"]] call ACV_FNCT_BroadcastMessage;
	};
	// if (call XfMissionACVEnabled) then {
		// // Creating using the ACV
		// if (isNil "_obj_dir") then {_obj_dir = 0;};
		// if (typename (_obj_params) == "ARRAY") then {
			// if (count _obj_params > 0) then {
				// if ((typename (_obj_params select 0) == "STRING") AND (_obj_action == "PICKUP")) then {_obj_name = _obj_params select 0;};
			// };
		// };
		// _obj_flying = 0;
		// If (_formation == "FLY") then { _obj_flying = 1; };
		// _acv_params = [];
		// _acv_params set [ACV_NET_PARAM_TYPE, 0];
		// _acv_params set [ACV_NET_PARAM_CLASS, _group_types];
		// _acv_params set [ACV_NET_PARAM_POS, _pos];
		// _acv_params set [ACV_NET_PARAM_DIR, random(360)];
		// _acv_params set [ACV_NET_PARAM_DISPLAYNAME, _obj_name];
		// _acv_params set [ACV_NET_PARAM_OWNER, "Mission"];
		// _acv_params set [ACV_NET_PARAM_SIDE, _newgroup];
		// _acv_params set [ACV_NET_PARAM_SKILL, (call XfGetUnitSkill)];
		// _acv_params set [ACV_NET_PARAM_RANK, "corporal"];
		// _acv_params set [ACV_NET_PARAM_FLYING, _obj_flying];
		// _acv_params set [ACV_NET_PARAM_OTHER, ""];
		// _acv_params set [ACV_NET_PARAM_SPECIAL, _formation];
		// if (_is_infantry) then {
			// _acv_params set [ACV_NET_PARAM_TYPE, ACV_NET_MAKEUNITS];
			// _newgroup = [_acv_params] call ACV_FNCT_MakeUnits;
		// } else {
			// {
				// // For vehicles we want a spot which is large enough
				// _pos = _pos findEmptyPosition[ 1 , 50 , _x];
				// if (count _pos == 0) then { _pos = _pos_center; };
				// _acv_params set [ACV_NET_PARAM_POS, _pos];
				// _acv_params set [ACV_NET_PARAM_CLASS, _x];
				// _acv_params set [ACV_NET_PARAM_TYPE, ACV_NET_MAKEVEHICLES];
				// _newgroup = [_acv_params] call ACV_FNCT_MakeVehicles;
				// sleep 1.1203;
			// } foreach _group_types;
		// };
		// _units = units _newgroup;
	// } else {
		// if (_is_infantry) then {
			// if (typename _group_types == "STRING") then {_group_types = [_group_types];};
			// _units = [_pos, _group_types, _newgroup] call x_makemgroup;
		// } else {
			// if (typename _group_types == "STRING") then {_group_types = [_group_types];};
			// {
				// _vecs = [1,_pos, _x, _newgroup,_radius,0] call x_makevgroup;
				// _units = [];
				// // We only want the vehicle(s).
				// _vehicles = [];
				// // The _vecs array is of the form: [[vec1, vec2, vec3], [all crews]]
				// _vecs = _vecs select 0;
				// for [{_i = 0},{_i < (count _vecs)},{_i = _i + 1}] do {
					// //if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3:  Veh:%4 ",diag_tickTime,"mission_functions.sqf","XfMissionCreateUnitGroup",_vecs select _i];};
					// if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3:  Veh:%4  Type:%5  Crew:%6",diag_tickTime,"mission_functions.sqf","XfMissionCreateUnitGroup",_vecs select _i,typeOf(_vecs select _i),crew(_vecs select _i)];};
					// _vehicles set [count _vehicles, _vecs select _i];
				// };
				// _units = [_units, _vehicles] call XfArrayPushStack;
			// } foreach _group_types;
		// };
	// };
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3:  Group:%4  Pos:%5  Side:%6  Units:%7",diag_tickTime,"mission_functions.sqf","XfMissionCreateUnitGroup",_newgroup,_pos,_group_side,_units];};
	_units call XfMissionRegisterUnits;
	_newgroup allowFleeing 0;
	if (count _upsmon > 0) then {
		_leader = leader _newgroup;
		// UPSMON needs more than the passed parameters. We add the necessary values.
		_upsmon = [[_leader],_upsmon] call XfArrayPushStack;
		_upsmon = _upsmon + ["spawned"];
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: UPSMON %4 (%5) Init:%6",diag_tickTime,"mission_functions.sqf","XfMissionCreateUnitGroup",_upsmon,_newgroup,_leader_init];};
		_upsmon execVM "scripts\UPSMON.sqf";
	} else {
		if (_taskpatrol) then {
			[_newgroup,_pos_center,_radius,_debug] call XfTaskPatrol;
			if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: TaskPatrol (%4)  Init:%5",diag_tickTime,"mission_functions.sqf","XfMissionCreateUnitGroup",_newgroup,_leader_init];};
		} else {
			if (_taskDefend) then {
				//This will activate the Static defenders. They will get a Guard waypoint + they'll occupy the nearest staticweapons if any and if empty.
				_newgroup setCombatMode "RED";
				if (_formation == "FORM") then {
					_newgroup setFormation (["COLUMN","STAG COLUMN","WEDGE","ECH LEFT","ECH RIGHT","VEE","LINE","DIAMOND"] call XfRandomArrayVal);
					_newgroup setFormDir (floor random 360);
				};
				_newgroup setSpeedMode "NORMAL";
				[_newgroup, getPos (leader _newgroup),_debug] spawn XfTaskDefend;
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: TaskDefend (%4)  Init:%5",diag_tickTime,"mission_functions.sqf","XfMissionCreateUnitGroup",_newgroup,_leader_init];};
			};
		};
	};
	if (_leader_init != "") then {
		_leader = leader _newgroup;
		_leader setVehicleInit _leader_init;
		processinitcommands;
	};
	_newgroup;
};

/// @FUNCTION
/// @NAME XfMissionCreateSnipersArea
/// @DESC Creates a single Group of units with the given initialization properties.
/// @ACCESS Server
/// @MP False
/// @PARAM String Side of the units
/// @PARAM Position A position which can be combined with a radius. By default the radius is 100.
/// @PARAM Scalar or Array The quantity interval of units to spawn or a scalar amount. @DEFAULT [1,2]
/// @PARAM Boolean A boolean indicating if we use TaskPatrol in case no UPSMON parameters were given. @DEFAULT True - Use TaskPatrol not TaskDefend.
/// @PARAM String The leader init line @DEFAULT ""
/// @PARAM String The unit special creation parameter (NONE, FORM, FLY, etc.) @DEFAULT "NONE"
/// @RETURN Object The created object
/// @REQUIRE Domination Template
/// @REMARK 
/// @EXAMPLE ["EAST",[position],[1,2]] call XfMissionCreateSnipersArea;
/// @EXAMPLE ["EAST",[[position],Radius],[5,5]] call XfMissionCreateSnipersArea;
XfMissionCreateSnipersArea = {
	private ["_side","_group_position","_quantity_interval","_quantity","_side_char","_radius","_pos_center","_pos","_sniper_units","_newgroup","_leader","_i","_debug","_unit"];
	_side = _this select 0;
	_group_position = _this select 1;
	_quantity_interval = [1,2];
	If (count _this > 2) then { _quantity_interval = _this select 2 };
	if (typeName _quantity_interval == "SCALAR") then {_quantity_interval = [_quantity_interval,_quantity_interval];};
	
	_pos_center = _group_position;
	_radius = 100;
	if (typename (_group_position select 0) == "ARRAY") then {
		_radius = _group_position select 1;
		_pos_center = _group_position select 0;
	};	
	_quantity = _quantity_interval select 0;
	If ( (_quantity_interval select 1) > _quantity) then {
		_quantity = round(_quantity + Random((_quantity_interval select 1) - _quantity));
	};
	if (d_mission_debug_messages) then { 
		diag_log "";
		diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionCreateSnipersArea",_this];
	};

	// We check if we need to spawn snipers or not.
	if (_quantity > 0) then {
		if (typeName _side != "STRING") then { _side = str(_side); };
		_side = toUpper(_side);
		_side_char = switch (_side) do {case "EAST": {"E"};case "WEST": {"W"};case "GUER": {"G"};case "CIV": {"W"}; default{"E"};};
		_sniper_units = [_side_char,1,2] call XfMissionGetRandomFootmenUnits;
		_upsmon = ["combat","red","nosmoke"];
		_building_pos_sniper = [_pos_center,_radius,"SNIPER"] call XfMissionBuildingAreaPosASL;
		// We check if there are buildings elligible for snipers.
		if (count _building_pos_sniper > 0) then {
			if (d_mission_debug_messages) then { 
				diag_log Format["(%1)[%2]: %3: %4: bld:%5  qun:%6",diag_tickTime,"mission_functions.sqf","XfMissionCreateSnipersArea","Creating Snipers",(count _building_pos_sniper),_quantity];
			};	
			_newgroup = [_side] call x_creategroup;
			_newgroup setCombatMode "RED";
			_newgroup setBehaviour "COMBAT";
			_newgroup setFormation "NONE";
			_building_pos_sniper = _building_pos_sniper call XfRandomArray;
			// We reduce the number of spawned snipers if there are not enough spots.
			If (count _building_pos_sniper < _quantity) then { _quantity = count _building_pos_sniper };
			
			for [{_i=0}, {_i < _quantity}, {_i=_i+1}] do
			{
				_unit_type = _sniper_units call XfRandomArrayVal;
				[_unit_type,_newgroup,_pos_center,"REGISTER",[],""] call XfMissionCreateMissionUnit;
			};
			for [{_i=0}, {_i < _quantity}, {_i=_i+1}] do
			{
				_unit = units _newgroup select _i;
				doStop _unit;
				// Will prevent the unit from moving and falling from roofs.
				_pos = _building_pos_sniper select _i;
				_unit setPosASL _pos;
				_unit setUnitPos "Middle";
				[_unit] call XfMissionStuckUnit;
				if (d_mission_debug_messages) then { 
					_marker = createMarkerLocal [Format["SniperMarker%1",_i], _pos];
					_marker setMarkerShapeLocal "ICON";
					_marker setMarkerTypeLocal "mil_triangle";
					_marker setMarkerColorLocal "ColorPink";
					_marker setMarkerTextLocal Format["S%1",_i];
				};	
			};
			if (d_mission_debug_messages) then { 
				diag_log Format["(%1)[%2]: %3: %4: %5",diag_tickTime,"mission_functions.sqf","XfMissionCreateSnipersArea","Created Snipers",_quantity];
			};	
		};
	};
	_newgroup;
};


// Returns a list of group types which can be instancied later.
// The goal is for example to randomly generate a list of groups which will defend a town.
// Knowing the current number of players, the enemy coeffs (setup.sqf) and the standard amounts (i_server.sqf)
//  we can generate a random list of units to spawn to keep the mission dynamic and to prevent knowing in advance
//  what the players will face.
// Parameters:
//		- EnemyPower = 1..5  Defines the power of the enemy in the place, 1 is the lowest amount and 5 the full power.
//		- Side = side of the group we'll want to create.. you can also populate a place with friendlies!
//		- Coeff = multiplies the amount of vehicles by this coeff. Rounded to 1 if lower.
XfMissionGetEnemyVehiclesArray = {
	private ["_power","_side","_coeff","_side_char","_vec","_vecCount","_new_units","_current_units","_unit_list","_i","_j","_k","_vehicle_array","_vehicle_side","_vecType","_vecTypeName","_vecPerGroup","_vec_list"];
	_power = _this select 0;
	_side = _this select 1;
	_coeff = _this select 2;
	_power = floor(_power);
	if (_power < 1) then {_power = 1;};
	if (_power > 5) then {_power = 5;};
	if (_coeff < 1) then {_coeff = 1;};
	_side = toUpper(str _side);
	_side_char = switch (_side) do {case "EAST": {"E"};case "WEST": {"W"};case "GUER": {"G"};case "CIV": {"W"}; default{"E"};};
	_only_uaz = false;
	if (count _this > 3) then { _only_uaz = _this select 3;};
	
	_unit_list = [];
	_vehicle_array = missionNamespace getVariable format ["d_vehicle_numbers_%1",_power];

	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: power:%4  side:%5  sidec:%6  coeff:%7  vear:%8 ",diag_tickTime,"mission_functions.sqf","XfMissionGetEnemyVehiclesArray",_power,_side, _side_char,_coeff,(count _vehicle_array)];};
	// Get the amounts of vehicles, this all is created from the arrays in i_server.sqf
	for [{_i = 0},{_i < count _vehicle_array},{_i = _i + 1}] do {
		_vec = _vehicle_array select _i;
		_vecCount = [_vec select 0, _coeff] call XfMissionGetRandomCount;

		if (_i <= 3) then { _vecCount = _vecCount + d_armor_base; } // Armored base (in setup.sqf)
		else { _vecCount = _vecCount + d_wheeled_base; }; // Wheeled base (in setup.sqf)
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: arr:%4  count:%5",diag_tickTime,"mission_functions.sqf","XfMissionGetEnemyVehiclesArray",_vec, _vecCount];};
		
		if (_vecCount > 0) then {
			For [{_k = 0;},{_k < _vecCount;},{_k = _k + 1;}] do {
				_vecType = round(_vec select 1);
				if (_only_uaz) then {_vecType = 5;};
				_vecTypeName = _vec select 2;
				_vecPerGroup = floor(_vec select 3);
				if (_vecPerGroup < 1) then {_vecPerGroup = 1};
				_current_units = [_side_char,_vecPerGroup,_vecType] call XfMissionGetRandomVehicleUnits;
				_unit_list set [count _unit_list, _current_units];
			};
			if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4: cnt:%5  type:%6  name:%7  grp:%8  units:%9",diag_tickTime,"mission_functions.sqf","XfMissionGetEnemyVehiclesArray","Adding units",_vecCount, _vecType, _vecTypeName, _vecPerGroup, _unit_list];};
		};	
	};	
	_unit_list;
};

// Returns a list of group types which can be instancied later.
// The goal is for example to randomly generate a list of groups which will defend a town.
// Knowing the current number of players, the enemy coeffs (setup.sqf) and the standard amounts (i_server.sqf)
//  we can generate a random list of units to spawn to keep the mission dynamic and to prevent knowing in advance
//  what the players will face.
// Parameters:
//		- EnemyPower = 1..5  Defines the power of the enemy in the place, 1 is the lowest amount and 5 the full power.
//		- Side = side of the group we'll want to create.. you can also populate a place with friendlies!
//		- Coeff = multiplies the amount of vehicles by this coeff. Rounded to 1 if lower.
XfMissionGetEnemyStaticsArray = {
	private ["_power","_side","_coeff","_side_char","_vec","_vecCount","_new_units","_current_units","_unit_list","_i","_j","_k","_vehicle_array","_vehicle_side","_vecType","_vecTypeName","_vecPerGroup","_vec_list"];
	_power = _this select 0;
	_side = _this select 1;
	_coeff = _this select 2;
	_power = floor(_power);
	if (_power < 1) then {_power = 1;};
	if (_power > 5) then {_power = 5;};
	if (_coeff < 1) then {_coeff = 1;};
	if (typename _side != "STRING") then { _side = str _side; };
	_side = toUpper(_side);
	_side_char = switch (_side) do {case "EAST": {"E"};case "WEST": {"W"};case "GUER": {"G"};case "CIV": {"W"}; default{"E"};};
	
	_unit_list = [];
	_vehicle_array = missionNamespace getVariable format ["d_statics_%1",_power];

	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: power:%4  side:%5  sidec:%6  coeff:%7  vear:%8",diag_tickTime,"mission_functions.sqf","XfMissionGetEnemyStaticsArray",_power,_side, _side_char,_coeff,(count _vehicle_array)];};
	// Get the amounts of vehicles, this all is created from the arrays in i_server.sqf
	for [{_i = 0},{_i < count _vehicle_array},{_i = _i + 1}] do {
		_vec = _vehicle_array select _i;
		_vecCount = [_vec select 0, _coeff] call XfMissionGetRandomCount;
		_vecCount = _vecCount + d_static_base; // from setup.sqf
		if (_vecCount > 0) then {
			For [{_k = 0;},{_k < _vecCount;},{_k = _k + 1;}] do {
				_vecType = round(_vec select 1);
				_vecTypeName = _vec select 2;
				_vecPerGroup = floor(_vec select 3);
				if (_vecPerGroup < 1) then {_vecPerGroup = 1};
				_current_units = [_side_char,_vecPerGroup,_vecType] call XfMissionGetRandomVehicleUnits;
				// In the particular case of Static units, we want to create only one array, because it avoids wasting groups and UPSMON scripts
				_unit_list = [_unit_list, _current_units] call XfArrayPushStack;
			};
			if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4: cnt:%5  type:%6  name:%7  grp:%8  units:%9",diag_tickTime,"mission_functions.sqf","XfMissionGetEnemyStaticsArray","Adding units",_vecCount, _vecType, _vecTypeName, _vecPerGroup, _unit_list];};
		};	
	};	
	_unit_list;
};

// Returns a list of group types which can be instancied later.
// The goal is for example to randomly generate a list of groups which will defend a town.
// Knowing the current number of players, the enemy coeffs (setup.sqf) and the standard amounts (i_server.sqf)
//  we can generate a random list of units to spawn to keep the mission dynamic and to prevent knowing in advance
//  what the players will face.
// Parameters:
//		- EnemyPower = 1..5  Defines the power of the enemy in the place, 1 is the lowest amount and 5 the full power.
//		- Side = side of the group we'll want to create.. you can also populate a place with friendlies!
//		- Coeff = multiplies the amount of troops by this coeff. Rounded to 1 if lower.
XfMissionGetEnemyTroopsArray = {
	private ["_group_kind","_power","_foot","_footCount","_new_units","_current_units","_unit_list","_i","_j","_k","_footmen_array","_footmen_side","_specops_side","_footType","_footTypeName","_nbrGroups","_foot_list","_vec"];
	_power = _this select 0;
	_side = _this select 1;
	_coeff = _this select 2;
	_power = floor(_power);
	if (_power < 1) then {_power = 1;};
	if (_power > 5) then {_power = 5;};
	if (_coeff < 1) then {_coeff = 1;};
	if (typename _side != "STRING") then { _side = str _side; };
	_side = toUpper(_side);
	_side_char = switch (_side) do {case "EAST": {"E"};case "WEST": {"W"};case "GUER": {"G"};case "CIV": {"W"}; default{"E"};};

	_unit_list = [];

	_footmen_array = missionNamespace getVariable format ["d_footunits_%1",_power];

	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: power:%4  side:%5  sidec:%6  coeff:%7  far:%8 ",diag_tickTime,"mission_functions.sqf","XfMissionGetEnemyTroopsArray",_power,_side, _side_char,_coeff,(count _footmen_array)];};
	// Get the amounts of footmen groups, this all is created from the arrays in i_server.sqf
	for [{_i = 0},{_i < count _footmen_array},{_i = _i + 1}] do {
		_vec = _footmen_array select _i;
		_footCount = [_vec select 0, _coeff] call XfMissionGetRandomCount;
		_footType = round(_vec select 1);
		if (_footType <= 0) then { _footCount = _footCount + d_footmen_base;}
		else { _footCount = round(_footCount + (d_footmen_base / 2));};
		
		if (_footCount > 0) then {
			For [{_k = 0;},{_k < _footCount;},{_k = _k + 1;}] do {
				_footTypeName = _vec select 2;
				_nbrGroups = floor(_vec select 3);
				if (_nbrGroups < 1) then {_nbrGroups = 1};
				_current_units = [_side_char,_nbrGroups,_footType] call XfMissionGetRandomFootmenUnits;
				_unit_list set [count _unit_list, _current_units];
			};
			if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4: cnt:%5  type:%6  name:%7  grp:%8  units:%9",diag_tickTime,"mission_functions.sqf","XfMissionGetEnemyTroopsArray","Adding units",_footCount, _footType, _footTypeName, _nbrGroups, _unit_list];};
		};	

	};
	_unit_list;
};

// Returns a list of unit names which can be used to instanciate units
XfMissionGetRandomFootmenUnits = {
	Private ["_side_char","_nbrOfGroups","_type","_footmen_side","_j","_new_units","_current_units","_specops_side"];
	_side_char = _this select 0;
	_nbrOfGroups = _this select 1;
	_type =0;
	if (count _this > 2) then {_type = floor(_this select 2)};
	if ((_type < 0) || (_type > 2)) then { _type = 0; };
	
	_nbrOfGroups = floor(_nbrOfGroups);
	If (_nbrOfGroups <= 0) then {_nbrOfGroups = 1}; 
	_current_units = [];
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: side:%4  nbr:%5  type:%6",diag_tickTime,"mission_functions.sqf","XfMissionGetRandomFootmenUnits",_side_char, _nbrOfGroups, _type];};
	If (_type == 0) then {
		_footmen_side = missionNamespace getVariable format ["d_allmen_%1",_side_char];
		// Case of basic units
		For [{_j = 0;},{_j < _nbrOfGroups;},{_j = _j + 1;}] do {
			_new_units = _footmen_side call XfRandomArrayVal;
			If (typename _new_units == "STRING") then { _new_units = [_new_units]; };
			_current_units = [_current_units, _new_units] call XfArrayPushStack;
		};				
	} else {
		If (_type == 1) then {
			_specops_side = missionNamespace getVariable format ["d_specops_%1",_side_char];
		} else {
			_specops_side = missionNamespace getVariable format ["d_snipers_%1",_side_char];
		};
		// Case of specops
		For [{_j = 0;},{_j < _nbrOfGroups;},{_j = _j + 1;}] do {
			_new_units = +_specops_side;
			If (typename _new_units == "STRING") then { _new_units = [_new_units]; };
			_current_units = [_current_units, _new_units] call XfArrayPushStack;
		};
	};
	_current_units
};

// Returns a list of unit names which can be used to instanciate units
XfMissionGetRandomVehicleUnits = {
	Private ["_side_char","_nbrOfGroups","_vecType","_vehicle_side","_j","_vec_list","_new_units","_current_units"];
	_side_char = _this select 0;
	_nbrOfGroups = _this select 1;
	_vecType = _this select 2;
	
	_nbrOfGroups = floor(_nbrOfGroups);
	If (_nbrOfGroups <= 0) then {_nbrOfGroups = 1};
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: side:%4  nbr:%5  type:%6",diag_tickTime,"mission_functions.sqf","XfMissionGetRandomVehicleUnits","Adding units",_side_char, _nbrOfGroups, _vecType];};
	_vehicle_side = missionNamespace getVariable format ["d_veh_a_%1",_side_char];
	
	_current_units = [];
	if (_vecType < 0) then {_vecType = 0};
	if (_vecType >= count _vehicle_side) then {
		_vecType = _vecType mod (count _vehicle_side);
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: Vehicle Type outside bounds (new:%4)",diag_tickTime,"mission_functions.sqf","XfMissionGetRandomVehicleUnits",_vecType];};
	};
	
	_vec_list = _vehicle_side select _vecType;
	For [{_j = 0;},{_j < _nbrOfGroups;},{_j = _j + 1;}] do {
		_new_units = _vec_list call XfRandomArrayVal;
		If (typename _new_units == "STRING") then { _new_units = [_new_units]; };
		_current_units = [_current_units, _new_units] call XfArrayPushStack;
	};
	_current_units
};


/// @FUNCTION
/// @NAME XfMissionCreateFootmenGroup
/// @DESC Creates a single Group of units with the given initialization properties.
/// @ACCESS Server
/// @MP False
/// @PARAM String Side of the units
/// @PARAM Position A position which can be combined with a radius. By default the radius is 100.
/// @PARAM Array The array of UPSMON initialization @DEFAULT [] This will trigger TaskPatrol.
/// @PARAM Scalar The type of troops to spawn: 0 = Standard, 1 = SpecOps, 2 = Snipers @DEFAULT 0 Standard troops
/// @PARAM Boolean A boolean indicating if we use TaskPatrol in case no UPSMON parameters were given. @DEFAULT True - Use TaskPatrol not TaskDefend.
/// @PARAM String The leader init line @DEFAULT ""
/// @PARAM String The unit special creation parameter (NONE, FORM, FLY, etc.) @DEFAULT "NONE"
/// @RETURN Object The created group
/// @REMARK 
/// @EXAMPLE ["WEST",[position],["noveh","ambush"]] call XfMissionCreateFootmenGroup;
/// @EXAMPLE ["WEST",[[position],Radius],["noveh","ambush"],1] call XfMissionCreateFootmenGroup;
/// @EXAMPLE ["WEST",[getPos _tower, 200],["noveh","ambush"],1] call XfMissionCreateFootmenGroup;
XfMissionCreateFootmenGroup ={
	private ["_side","_position","_radius","_upsmon","_type","_taskpatrol","_leader_init","_side_char","_current_units","_new_group","_side_str"];
	_side = _this select 0;
	_position = _this select 1;
	_radius = 100;
	if (typeName (_position select 0) == "ARRAY") then {
		_radius = _position select 1;
		_position = _position select 0;
	};
	_upsmon = [];
	_side_str = d_enemy_side;
	If (count _this > 2) then {	_upsmon = _this select 2; };
	_type = 0;
	if (count _this > 3) then {	_type = _this select 3; };
	_taskpatrol = true;
	if (count _this > 4) then {	_taskpatrol = _this select 4; };
	_leader_init = "";
	if (count _this > 5) then {_leader_init = _this select 5; };
	if (typeName _side == "STRING") then {
		_side_str = toUpper(_side);
	};
	// Case when we gave a side variable
	if ((typename _side != "STRING") && (_side in [east,west,resistance,civilian])) then {
		_side_str = str _side;
		_side = toUpper(_side_str);
	};
	// Case when we gave a group
	if (typeName _side != "STRING") then {
		_side_str = str(side _side);
	};
	_side_str = toUpper(_side_str);
	_side_str = switch (_side_str) do {case "EAST": {"EAST"};case "WEST": {"WEST"};case "GUER": {"GUER"};case "CIV": {"GUER"}; default{d_enemy_side};};
	_side_char = switch (_side_str) do {case "EAST": {"E"};case "WEST": {"W"};case "GUER": {"G"};case "CIV": {"G"}; default{"E"};};
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: sidestr:%4  pos:%5  rad:%6  spec:%7  ups:%8  side:%9",diag_tickTime,"mission_functions.sqf","XfMissionCreateFootmenGroup",_side_str,_position,_radius,_specops,_upsmon,_side];};

	_current_units = [_side_char,1,_type] call XfMissionGetRandomFootmenUnits;
	_new_group = objNull;
	if (count _current_units > 0) then {
		_new_group = [_current_units,true,_side,[_position,_radius],_upsmon,_taskpatrol] call XfMissionCreateUnitGroup;
	};	
	_new_group
};

/// @FUNCTION
/// @NAME XfMissionCreateVehicleGroup
/// @DESC Creates a single Vehicle and it's crew with the given initialization properties.
/// @ACCESS Server
/// @MP False
/// @PARAM String Side of the units
/// @PARAM Position A position which can be combined with a radius. By default the radius is 100.
/// @PARAM Array The array of UPSMON initialization @DEFAULT [] This will trigger TaskPatrol.
/// @PARAM String The type of vehicle to spawn (either the numeric type for i_server.sqf or the string type or an array of string types)
/// @PARAM Boolean A boolean indicating if we use TaskPatrol in case no UPSMON parameters were given. @DEFAULT True - Use TaskPatrol not TaskDefend.
/// @PARAM String The leader init line @DEFAULT ""
/// @PARAM Boolean A boolean indicating if we use TaskDefend is activated as last resort. @DEFAULT True - Use TaskDefend.
/// @RETURN Object The created group
/// @REMARK 
/// @EXAMPLE ["WEST",[position],Radius,["onroad","aware"],"BRDM2",true,""] call XfMissionCreateVehicleGroup;
/// @EXAMPLE ["WEST",[[position],Radius],["onroad","aware"],"BRDM2",true,""] call XfMissionCreateVehicleGroup;
/// @EXAMPLE ["WEST",[_tower_pos,60],["onroad","aware"],"BRDM2",false,"", false] call XfMissionCreateVehicleGroup;
XfMissionCreateVehicleGroup ={
	private ["_side","_position","_radius","_upsmon","_vec_type","_taskpatrol","_leader_init","_side_char","_current_units","_new_group"];
	_side = _this select 0;
	_position = _this select 1;
	_upsmon = _this select 2;	
	_vec_type = _this select 3;
	_taskpatrol = true;
	_taskdefend = true;
	_radius = 100;
	if (typeName (_position select 0) == "ARRAY") then {
		_radius = _position select 1;
		_position = _position select 0;
	};
	if (count _this > 4) then {	_taskpatrol = _this select 4; };
	_leader_init = "";
	if (count _this > 5) then {_leader_init = _this select 5; };
	if (count _this > 6) then {_taskdefend = _this select 6; };
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: side:%4  pos:%5  rad:%6  type:%7  ups:%8",diag_tickTime,"mission_functions.sqf","XfMissionCreateVehicleGroup",_side,_position,_radius,_vec_type,_upsmon];};
	
	if (typeName _vec_type == "SCALAR") then {
		if (typename _side != "STRING") then { _side = str _side; };
		_side = toUpper(_side);
		_side_char = switch (_side) do {case "EAST": {"E"};case "WEST": {"W"};case "GUER": {"G"};case "CIV": {"W"}; default{"E"};};
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionCreateVehicleGroup","Getting Random Units"];};
		_current_units = [_side_char,1,_vec_type] call XfMissionGetRandomVehicleUnits;
	};
	if (typeName _vec_type == "STRING") then {
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionCreateVehicleGroup","Type to array"];};
		_current_units = [_vec_type];
	};
	if (typeName _vec_type == "ARRAY") then {
		_current_units = _vec_type;
	};
	if (count _current_units > 0) then {
		_new_group = [_current_units,false,_side,[_position,_radius],_upsmon,_taskpatrol,_leader_init,"FORM",_taskdefend] call XfMissionCreateUnitGroup;
	};
	_new_group
};

/// @FUNCTION
/// @NAME XfMissionPopulateLocation
/// @DESC Fills a zone with enemy units
/// @ACCESS Server
/// @MP False
/// @PARAM String Side of the units
/// @PARAM Position A position which can be combined with a radius. By default the radius is 200.
/// @PARAM Boolean Whether to use or not UPSMon to handle the units. Otherwise it's TaskPatrol @DEFAULT True
/// @PARAM Scalar Override for the power value. This can be used to override the default porportional to players amounts. @DEFAULT 0 = no override
/// @PARAM Scalar Override for the coeff value. This can be used to override the default porportional to players amounts. @DEFAULT 0 = no override
/// @PARAM Scalar Increment for the power value. This can be used to Increment the default porportional to players amounts. @DEFAULT 0 = no Increment
/// @PARAM Scalar Increment for the coeff value. This can be used to Increment the default porportional to players amounts. @DEFAULT 0 = no Increment
/// @PARAM Boolean Replace all vehicles with UAZ. @DEFAULT False = Do not replace
/// @RETURN Nothing
/// @REMARK The Coeff value enters in the Power calculation and will have an effect on the size of groupe. The higher the coeff is, the larger the groups are.
/// @REMARK Power ranges from 1 to 5. Coeff ranges from 1 to 4.
/// @EXAMPLE ["EAST",[[position],Radius]] call XfMissionPopulateLocation; // Typical case for enemy locations
/// @EXAMPLE ["WEST",[[position],Radius],false] call XfMissionPopulateLocation; // TaskPatrol is lighter when filling ally locations
XfMissionPopulateLocation = {
	private ["_troops_side","_troops_power","_troops_coeff","_center_location","_radius","_radius_coeff","_use_upsmon","_footmen","_statics","_vehicles","_footmen_options","_statics_options","_vehicles_options","_ups_mon_marker","_all_groups","_grp","_marker_alpha","_power_override","_power_increment","_coeff_override","_coeff_increment","_curr_radius","_total_count","_min_radius"];
	_troops_side = _this select 0;
	_center_location = _this select 1;
	_radius = 200;
	_use_upsmon = true;
	if (count _this > 2) then {	_use_upsmon = _this select 2; };
	_power_override = 0;
	if (count _this > 3) then {_power_override = _this select 3;};
	_coeff_override = 0;
	if (count _this > 4) then {_power_override = _this select 4;};
	_power_increment = 0;
	_coeff_increment = 0;
	if (count _this > 5) then {_power_increment = _this select 5;};
	if (count _this > 6) then {_coeff_increment = _this select 6;};
	_only_uaz = false;
	if (count _this > 7) then {_only_uaz = _this select 7;};
	
	
	if (typeName (_center_location select 0) == "ARRAY") then {
		_radius = _center_location select 1;
		_center_location = _center_location select 0;
	};
	
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: Start: %4",diag_tickTime,"mission_functions.sqf","XfMissionPopulateLocation",_this];};
	
	_players = call XPlayersNumber;
	_troops_power = 1; 
	_troops_coeff = 1;
	switch (true) do {
		case (_players <= 5): { _troops_power = 1 };
		case (_players <= 10): { _troops_power = 2 };
		case (_players <= 15): { _troops_power = 3 };
		case (_players <= 20): { _troops_power = 4 };
		default {_troops_power = 5;};
	};
	// For a larger area, we want more troops
	// _radius_coeff = floor(_radius / 200);
	// _troops_power = _troops_power + _radius_coeff;
	if (_radius > 250) then { _troops_power = _troops_power + 1; };
	
	if (!isnil "DM_Mission_EnemyPower") then { _troops_power = _troops_power + DM_Mission_EnemyPower; };
	if (!isnil "DM_Mission_EnemyCoeff") then { _troops_coeff = _troops_coeff + DM_Mission_EnemyCoeff; };
	if (_power_override > 0) then {_troops_power = _power_override};
	if (_coeff_override > 0) then {_troops_coeff = _coeff_override};
	_troops_power = _troops_power + _power_increment;
	_coeff_override = _coeff_override + _coeff_increment;
	if (_troops_power > 5) then {_troops_power = 5;};
	if (_troops_coeff > 4) then {_troops_coeff = 4;};
	
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: Power: %4   Coeff:%5  (override:%6)",diag_tickTime,"mission_functions.sqf","XfMissionPopulateLocation",_troops_power,_troops_coeff,((_power_override > 0) || (_coeff_override > 0))];};
	
	// This enlarges the target location if the troop coefficient has been raised a lot. Otherwise the units are just walking on top of each others.
	If ((_radius <= 250) And (_troops_power > 2)) then {
		_radius = _radius * (1 + ((_troops_power + _troops_coeff) / 3.5));
		If (_radius > 370) then { _radius = 370 };
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: Radius: %4",diag_tickTime,"mission_functions.sqf","XfMissionPopulateLocation",_radius];};
	};
	
	_footmen = [_troops_power,_troops_side,_troops_coeff] call XfMissionGetEnemyTroopsArray;
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: Footmen: %4",diag_tickTime,"mission_functions.sqf","XfMissionPopulateLocation",_footmen];};
	_statics = [_troops_power,_troops_side,_troops_coeff] call XfMissionGetEnemyStaticsArray;
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: Statics: %4",diag_tickTime,"mission_functions.sqf","XfMissionPopulateLocation",_statics];};
	_vehicles = [_troops_power,_troops_side,_troops_coeff,_only_uaz] call XfMissionGetEnemyVehiclesArray;
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: Vehicles: %4",diag_tickTime,"mission_functions.sqf","XfMissionPopulateLocation",_vehicles];};
	
	_total_count = count _footmen + count _statics + count _vehicles;
	_min_radius = _total_count * 20;
	if (_min_radius > 250) then { _min_radius = 250; };
	_radius = _min_radius max _radius;
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: Group Total Count: %4   Minimum Radius : %5   Resulting Radius : %6",diag_tickTime,"mission_functions.sqf","XfMissionPopulateLocation",_total_count, _min_radius, _radius];};
	
	_footmen_options = [];
	_statics_options = [];
	_vehicles_options = [];
	if (_use_upsmon) then {
		_ups_mon_marker = Format["upsmon_marker_%1",(call XfMissionNextJipID)];
		_marker_alpha = 0.001;
		If (d_mission_debug_messages) then {_marker_alpha = 0.6;};
		[true,[_ups_mon_marker, _center_location, "ELLIPSE", "ColorBlue", [_radius,_radius],"",0,"","",_marker_alpha]] call XfMissionMarker;
		_footmen_options = [_ups_mon_marker,"noveh","aware","buildings","RED"];
		_statics_options = [_ups_mon_marker,"combat","RED"];
		_vehicles_options = [_ups_mon_marker,"onroad","aware","RED"];
	};
	_all_groups = [];
	{
		_grp = [_x,true,_troops_side,[_center_location,_radius],_footmen_options] call XfMissionCreateUnitGroup;
		_all_groups set [Count _all_groups, _grp];
	} foreach _footmen;
	if (d_mission_debug_messages) then {
		diag_log Format["(%1)[%2]: %3: Footmen created",diag_tickTime,"mission_functions.sqf","XfMissionPopulateLocation"];
		diag_log "";
	};
	// In the case of statics they will all belong to the same group
	_grp = [_troops_side] call x_creategroup;
	// The following avoids a static to find itself all alone far from the centre.
	_curr_radius = _radius;
	if (_curr_radius > 100) then {_curr_radius = _radius / 2;};
	if (_curr_radius < 100) then {_curr_radius = 100;};
	{
		_grp = [_x,false,_grp,[_center_location,_curr_radius],_statics_options] call XfMissionCreateUnitGroup;
	} foreach _statics;
	_all_groups set [Count _all_groups, _grp];
	if (d_mission_debug_messages) then {
		diag_log Format["(%1)[%2]: %3: Statics created",diag_tickTime,"mission_functions.sqf","XfMissionPopulateLocation"];
		diag_log "";
	};	
	{
		_grp = [_x,false,_troops_side,[_center_location,_radius],_vehicles_options] call XfMissionCreateUnitGroup;
		_all_groups set [Count _all_groups, _grp];
	} foreach _vehicles;
	if (d_mission_debug_messages) then {
		diag_log Format["(%1)[%2]: %3: Vehicles created",diag_tickTime,"mission_functions.sqf","XfMissionPopulateLocation"];	
		diag_log "";
	};
	_all_groups;
};

// Return a count of all units in a zone in the format:
// [[west total, [west men, west veh, west ship, west air]], [east total, [east men, east veh, east ship, east air]], [guer total, [guer men, guer veh, guer ship, guer air]]]
XfMissionGetZoneAllSidesCount = {
	private ["_center","_radius","_i","_result","_result_west","_result_east","_result_guer","_entities","_west_total_count","_east_total_count","_guer_total_count", "_west_count","_east_count","_guer_count"];
	_center = _this select 0;
	_radius = _this select 1;

	_west_total_count = 0;
	_east_total_count = 0;
	_guer_total_count = 0;
	
	_result = [];
	_result_west = [_west_total_count,[]];
	_result_east = [_east_total_count,[]];
	_result_guer = [_guer_total_count,[]];

	// if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionGetZoneAllSidesCount", _this];};

	_i = 0;
	{
		_west_count = 0;
		_east_count = 0;
		_guer_count = 0;
		_entities = _center nearEntities [[_x],_radius];
		// if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 = %5",diag_tickTime,"mission_functions.sqf","XfMissionGetZoneAllSidesCount", _x, (count _entities)];};
		{
			If (alive _x) then {
				switch (side _x) do {
					case west: {
						_west_count = _west_count + 1;
						_west_total_count = _west_total_count + 1;
					};
					case east: {
						_east_count = _east_count + 1;
						_east_total_count = _east_total_count + 1;
					};
					case resistance: {
						_guer_count = _guer_count + 1;
						_guer_total_count = _guer_total_count + 1;
					};
				};
			};
		}foreach _entities;
		_result_west set [0, _west_total_count];
		_result = _result_west select 1;
		_result set [_i, _west_count];
		_result_west set [1, _result];
		
		_result_east set [0, _east_total_count];
		_result = _result_east select 1;
		_result set [_i, _east_count];
		_result_east set [1, _result];
		
		_result_guer set [0, _guer_total_count];
		_result = _result_guer select 1;
		_result set [_i, _guer_count];
		_result_guer set [1, _result];
		
		_i = _i + 1;
	} foreach ["Man","LandVehicle","Ship","Air"];
	
	_result = [_result_west,_result_east,_result_guer];
	// if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: Entities count: %4",diag_tickTime,"mission_functions.sqf","XfMissionGetZoneAllSidesCount", _result];};
	_result
};

/// @FUNCTION
/// @NAME XfMissionGetZoneSideCount
/// @DESC Counts all the units present in a zone, this can be used to trigger a reinforcement event when the unit count drops too low.
/// @ACCESS Public
/// @MP False
/// @PARAM Position The center of the target area
/// @PARAM Number The size of the target area @DEFAULT 100
/// @PARAM String The side we want to count @DEFAULT Own Side
/// @RETURN Array The total number of units in the form: [side_total, [side_men, side_veh, side_ship, side_air]]
/// @REMARK
/// @EXAMPLE [[pos],radius,"EAST"] call XfMissionGetZoneSideCount
XfMissionGetZoneSideCount = {
	private ["_center","_radius","_side","_result"];
	_center = _this select 0;
	_radius = 100;
	if (count _this > 1) then { _radius = _this select 1; };
	_side = d_own_side;
	if (count _this > 2) then { _side = _this select 2; };

	if (typename _side != "STRING") then { _side = str _side; };
	_side = toUpper(_side);

	// if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionGetZoneSideCount", _this];};
	Switch (_side) do {
		case "WEST":{_result = ([_center,_radius] call XfMissionGetZoneAllSidesCount) select 0;};
		case "EAST":{_result = ([_center,_radius] call XfMissionGetZoneAllSidesCount) select 1;};
		case "GUER":{_result = ([_center,_radius] call XfMissionGetZoneAllSidesCount) select 2;};
	};
	// if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: Entities count: %4",diag_tickTime,"mission_functions.sqf","XfMissionGetZoneSideCount", _result];};
	_result
};


/// @FUNCTION
/// @NAME XfMissionGetRandomCount
/// @DESC Returns a random number between two bounds. You can also multiply the bounds by a coefficient (bigger than 1) and return the result
/// @ACCESS Public
/// @MP True
/// @PARAM Array The lower and upper bounds of the random value
/// @PARAM Number A multiplier for the bounds @DEFAULT 1
/// @PARAM String The side we want to count @DEFAULT Own Side
/// @RETURN Array The total number of units in the form: [side_total, [side_men, side_veh, side_ship, side_air]]
/// @REMARK
/// @EXAMPLE [[lowbound,upbound],mult] call XfMissionGetRandomCount
XfMissionGetRandomCount = {
	private ["_minmax","_coeff","_return","_max","_min","_base_value"];
	_minmax = _this select 0;
	_coeff = 1;
	If (count _this > 1) then { _coeff = _this select 1; };
	if (_coeff < 1) then {_coeff = 1;};
	_return = 0;
	_max = _minmax select 0;
	if (_max == 0 and _coeff >= 2) then {_max = 1;};
	_min = _minmax select 1;
	if (_max > 0) then {
		if (_min > _max) then {_min = _max};
		_min = round(_min * _coeff);
		if ((_min <= 0) AND ((_minmax select 1) > 0)) then {_min = 1};
		_max = round(_max * _coeff);
		if ((_max <= 0) AND ((_minmax select 0) > 0)) then {_max = 1};
		if (_min > _max) then {_min = _max};
		_base_value = _min;
		if (_max > _min) then {_base_value = floor(random((_max - _min) + 1)) + _min;};
		_return = _base_value;
	};
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4  (%5,%6,%7,%8)",diag_tickTime,"mission_functions.sqf","XfMissionGetRandomCount",_return,_minmax,_max,_min,_coeff];};
	_return;
};

/// @FUNCTION
/// @NAME XfMissionCallReinforcement
/// @DESC Triggers the call for reinforcement units. It can be called from any computer.
/// @ACCESS Public
/// @MP True
/// @PARAM String The side of the reinforcement to create: EAST, WEST, etc.
/// @PARAM Position The center of the target area. You can combine it with a radius otherwise it's 100 as radius.
/// @PARAM String The type of reinforcement: "VEH","MEN","AIR","PARA","SEA" @DEFAULT "PARA"
/// @PARAM Scalar Power of the reinforcement, a value between 1 and 5 @DEFAULT 1
/// @PARAM Scalar The type of unit we want to create. It is dependent on the server config as which values are possible. @DEFAULT -1 Random
/// @PARAM Position The position where the units should spawn. @DEFAULT [] Random
/// @PARAM Scalar The unique ID created on the client @DEFAULT Given by the system, don't fill it!
/// @RETURN Scalar The unique ID of the reinforcement, you need to give it to the cancel function if you want to cancel a running reinforcement.
/// @REMARK It is recommended NOT TO USE the last three parameters. You can do it but at your own risk. It might limit the randomness of your mission and the future compatibility.
/// @REMARK The last parameter, the unique ID should not be modified unless you really know what you are doing...
/// @EXAMPLE ["SIDE",[[pos],radius],"PARA"] call XfMissionCallReinforcement
XfMissionCallReinforcement = {
	private ["_side","_center","_radius","_type","_power","_unittype","_start_pos","_unique_id"];
	_side = _this select 0;
	_center = _this select 1;
	_radius = 100;
	_unittype = -1;
	_start_pos = [];
	_unique_id = 0;
	if (typename (_center select 0) == "ARRAY") then {
		_radius = _center select 1;
		_center = _center select 0;
	};
	_type = "PARA";
	if (count _this > 2) then { _type = toUpper(_this select 2); };
	_power = 1;
	if (count _this > 3) then { _power = floor(_this select 3); };
	if (_power < 1) then { _power = 1 };
	if (_power > 5) then { _power = 5 };
	if (count _this > 4) then { _unittype = round(_this select 4); };
	if (count _this > 5) then { _start_pos = _this select 5; };
	if (count _this > 6) then { _unique_id = floor(_this select 6); };
	if (d_mission_debug_messages) then { Format["Calling Reinforcement: %1 (%2)",_type,_power] call XfMissionMessageToAll; };
	if (_type == "AIR") then {
		// In the case of Air Reinforcement, it's simply air support
		_unique_id = [_side,[_center,_radius],_power,_start_pos,_unique_id] call XfMissionCallAirSupport;
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 (%5,%6)",diag_tickTime,"mission_functions.sqf","XfMissionCallReinforcement","Called reinforcements",_type,_power];};
	} else {
		If (_unique_id <= 0) then { _unique_id = call XfMissionNextJipID; };
		["d_s_mreinf",[_side,_center,_radius,_type,_power,_unittype,_start_pos,_unique_id]] call XNetCallEvent;
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 (%5,%6)",diag_tickTime,"mission_functions.sqf","XfMissionCallReinforcement","Called reinforcements",_type,_power];};
	};
	_unique_id;
};

/// @FUNCTION
/// @NAME XfMissionCancelReinforcement
/// @DESC Cancels the call for reinforcement units. If reinforcement is already on the way, it will be destroyed.
/// @ACCESS Public
/// @MP True
/// @PARAM Scalar The unique ID of the reinforcement to cancel
/// @RETURN Nothing
/// @REMARK
/// @EXAMPLE [_unique_id] call XfMissionCancelReinforcement
XfMissionCancelReinforcement = {
	["reinforcement_canceled",true] call XNetSetJIP;
};


/// @FUNCTION
/// @NAME XfMissionRandomReinforcement
/// @DESC When used inside a mission, this will check if a reinforcement can be sent on a time interval basis which is modulated by the player number.
///       The reinforcement will be of a random type (PARA, MEN, VEH)
/// @ACCESS Server
/// @MP False
/// @PARAM String The side of the reinforcement to create: EAST, WEST, etc.
/// @PARAM Position The center of the target area. You can combine it with a radius otherwise it's 100 as radius.
/// @PARAM Scalar The Reinforcement intensity. Normally you will pass the mission variable which is set in the start mission screen. @DEFAULT 0 No reinforcement
/// @PARAM String The type of reinforcement to send if you want to specify it. @DEFAULT "RANDOM"
/// @PARAM Boolean Indicates if the reinforcement should be immediate or not. @DEFAULT False - Wait the required time before a reinforcement can occur (usually around 10 minutes)
/// @RETURN Scalar The unique ID of the reinforcement to cancel
/// @REMARK 
/// @EXAMPLE ["SIDE",[[pos],radius],Power,"RANDOM",False] call XfMissionRandomReinforcement
XfMissionRandomReinforcement = {
	private ["_name","_side","_center","_radius","_type","_power","_last_random_reinforcement","_random_reinf_diff","_time_coeff","_spawn_coeff","_immediate"];
	_unique_id = 0;
	if (isServer) then {
		private ["_side","_position","_radius","_coeff","_spawn_pos"];
		_side = toUpper(_this select 0);
		_center = _this select 1;
		_radius = 100;
		_immediate = false;
		if (typename (_center select 0) == "ARRAY") then {
			_radius = _center select 1;
			_center = _center select 0;
		};
		if (isnil "DM_Mission_RandomReinforcement") then {DM_Mission_RandomReinforcement = 0;};
		_power = round(DM_Mission_RandomReinforcement / 2.5);
		if (count _this > 2) then {_power = _this select 2};
		_type = "RANDOM";
		if (count _this > 3) then { _type = toUpper(_this select 3); };
		if (count _this > 4) then {
			_immediate = _this select 4;
			if (toUpper(Format["%1",_immediate]) == "TRUE") then {
				_immediate = true;
			} else {
				_immediate = false;
			};
		};

		_name = Format["DM_Timer_%1_%2_%3",_side, round(_center select 0), round(_center select 1)];
		if (isnil _name) then {
			missionNamespace setVariable [_name,0];
		};
		_last_random_reinforcement = missionNamespace getVariable _name;
		_random_reinf_diff = time - _last_random_reinforcement;
		If (_random_reinf_diff <= 0) then {_random_reinf_diff = 1;};
		_time_coeff = 3600;_spawn_coeff = 0;
		switch (true) do {
			case ((DM_Mission_RandomReinforcement max _power) == 1): {_time_coeff = 900;_spawn_coeff = 0.7;};
			case ((DM_Mission_RandomReinforcement max _power) == 2): {_time_coeff = 800;_spawn_coeff = 0;};
			case ((DM_Mission_RandomReinforcement max _power) == 3): {_time_coeff = 500;_spawn_coeff = 0.7;};
			case ((DM_Mission_RandomReinforcement max _power) >= 4): {_time_coeff = 420;_spawn_coeff = 0;};
			case ((DM_Mission_RandomReinforcement max _power) >= 7): {_time_coeff = 420;_spawn_coeff = 0;};
			default {_time_coeff = 3600;_spawn_coeff = 0;};
		};

		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionRandomReinforcement",[_name,_last_random_reinforcement,_random_reinf_diff,_time_coeff,_spawn_coeff,DM_Mission_RandomReinforcement,_immediate]];};
		if (_immediate || (_random_reinf_diff >= _time_coeff)) then {
			if (_immediate || (random(1) >= _spawn_coeff)) then {
				hint "Random Reinforcement";
				missionNamespace setVariable [_name,time];
				if (_power < 1) then { _power = 1 };
				if (_power > 5) then { _power = 5 };

				if (!(_type in ["PARA","MEN","VEH","MAN"])) then { _type = "RANDOM" };
				// Vehicles get a lesser chance... to make it more bearable for human players
				if (_type == "RANDOM") then {_type = ["PARA","MEN","VEH","MEN","PARA"] call XfRandomArrayVal;};
				if (_type == "VEH" && _power > 3) then { _power = 3; };
				_unique_id = [_side,[_center,_radius],_type,_power,-1,[],_unique_id] call XfMissionCallReinforcement;
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3; %4 %5",diag_tickTime,"mission_functions.sqf","XfMissionRandomReinforcement","Random reinforcement sent!",[_side,[_center,_radius],_type,_power,-1,[],_unique_id]];};
			};
		} else {
			if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3; %4 (%5 < %6)",diag_tickTime,"mission_functions.sqf","XfMissionRandomReinforcement","Random reinforcement not sent", _random_reinf_diff, _time_coeff];};
		};
	};
	_unique_id;
};


/// @FUNCTION
/// @NAME XfMissionCallAirSupport
/// @PACKAGE MissionFramework
/// @DESC Calls air support from a side on a given position.
/// @ACCESS Public
/// @MP True
/// @PARAM String The side of the air support: EAST, WEST, etc.
/// @PARAM Position The position where to send the air support. The position can be given in the format [[pos],radius] By default the radius will be 200.
/// @PARAM Scalar The coefficient to tweak the air support amount. It must be a value between 0 and 4. Not that the amount is always randomized in all cases.
/// @PARAM Position The position where the units will spawn. @DEFAULT A random point around the map border.
/// @PARAM Scalar The unique ID created on the client @DEFAULT Given by the system, don't fill it!
/// @RETURN Scalar The unique ID of the Air support
/// @REMARK The last parameter, the unique ID should not be modified unless you really know what you are doing...
/// @EXAMPLE ["SIDE",[[pos],Radius],Coeff] call XfMissionCallAirSupport
XfMissionCallAirSupport = {
	_unique_id = 0;
	if (isServer) then {
		private ["_side","_position","_radius","_coeff","_spawn_pos"];
		_side = toUpper(_this select 0);
		_position = _this select 1;
		_radius = 200;
		_spawn_pos = [];
		if (typeName (_position select 0) == "ARRAY") then {
			_radius = _position select 1;
			_position = _position select 0;
		};
		if (_radius < 150) then {_radius = 150};
		_coeff = DM_Mission_AirSupport;
		if (count _this > 2) then { _coeff = _this select 2; };
		if (_coeff < 0) then {_coeff = 0;};
		if (_coeff > 4) then {_coeff = 4;};
		if (count _this > 3) then {_spawn_pos = _this select 3;};
		if (count _this > 4) then {_unique_id = _this select 4;};
		If (_unique_id <= 0) then { _unique_id = call XfMissionNextJipID; };
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionCallAirSupport",_this];};
		[_side,[_position,_radius],_coeff,_spawn_pos,_unique_id] spawn XfMissionAirSupport;
	} else {
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionCallAirSupport Event",_this];};
		_unique_id = call XfMissionNextJipID;
		_this = _this + [_unique_id];
		["d_s_mexecute", ["XfMissionCallAirSupport",_this]] call XNetCallEvent;
	};
	_unique_id;
};


XfMissionRegisterUnits = {
	private ["_veh"];
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionRegisterUnits",_this];};
	if (typename _this == "ARRAY") then {
		{
			_x call XfMissionRegisterUnits;
		} foreach _this;
	} else {
		_veh = vehicle _this;
		_action = toUpper(_veh call XfMissionGetAction);
		if (_action != "UNKNOWN" && _action != "") then {
			_veh = _this;
		};
		[_veh,"REGISTER"] call XfMissionSetAction;
		_veh call XfMissionRegisterObject;
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4: %5 (%6)",diag_tickTime,"mission_functions.sqf","XfMissionRegisterUnits","Registering",_veh, (typeOf _veh)];};
		if ((_veh isKindof "LandVehicle") OR (_veh isKindof "Air") OR (_veh isKindof "Ship")) then {
			{
				_x call XfMissionRegisterUnits;
			} foreach crew _veh;
		};
	};
};

/// @FUNCTION
/// @NAME XfMissionCreateBox
/// @DESC Creates a box like those dropped by a MHQ or another unit.
/// @ACCESS Public
/// @MP True
/// @PARAM Position Where the box should be created
/// @PARAM String Side of the box to create
/// @PARAM Class The object type of the box to customize the look
/// @RETURN Nothing
/// @REQUIRE Domination Template
/// @REMARK
/// @EXAMPLE [getPos player, "WEST", "USOrdnanceBox"] call XfMissionCreateBox
XfMissionCreateBox = {
	private ["_boxpos","_side","_type","_marker_name"];
	_boxpos = _this select 0;
	_side = d_own_side;
	if (count _this > 1) then {
		_side = _this select 1; 
		// Controls that the value is one of the acceptable ones
		d_side_player = switch (_side) do {
			case "EAST": {_side};
			case "WEST": {_side};
			case "GUER": {d_own_side};
			default {d_own_side};
			};
	};
	_type = d_air_drop_box;
	if (count _this > 2) then {
		_type = _this select 2;
		if ((d_all_ammoboxes find _type) < 0) then {
			_type = d_air_drop_box;
		};
	};
	_marker_name = Format["missionbox%1%2%3",floor(_boxpos select 0),floor(_boxpos select 1),floor(_boxpos select 2)];
	["d_air_box", [_boxpos, "MissionBox", _marker_name,_side,_type]] call XNetCallEvent;
};


/// @FUNCTION
/// @NAME XfMissionConvergeUnits
/// @DESC This function will make a given number of groups from the given side to converge to the given position in the given radius. This will be useful mostly to make enemies converge on a target position after it was destroyed or taken by the player for example.
/// @ACCESS Server
/// @MP False
/// @PARAM Position Where the groups should converge (can add a Radius, otherwise it's 30 by default)
/// @PARAM String Side of the groups which should converge
/// @PARAM Scalar The radius in which to search for units @DEFAULT 200 or KRON_UPS_sharedist
/// @PARAM Array The type of objects which should converge @DEFAULT ["Man","Car","MotorCycle","Tank"]
/// @PARAM Scalar The number of groups to call @DEFAULT 3
/// @RETURN Scalar The number of groups coming
/// @REQUIRE Domination Template
/// @REMARK
/// @EXAMPLE [getPos player, "WEST", 200] call XfMissionConvergeUnits;
XfMissionConvergeUnits = {
	private ["_position","_side","_radius","_types","_nbr_grps","_entities","_grps","_wp","_grp"];
	_position = _this select 0;
	_zone = 30;
	_side = _this select 1;
	_radius = 200;
	if (!isNil "KRON_UPS_sharedist") then {_radius = KRON_UPS_sharedist;};
	_types = ["Man","Car","Motorcycle","Tank"];
	_nbr_grps = 3;
	if (count _this > 2) then {_radius = _this select 2;};
	if (_radius < 100) then {_radius = 100;};

	if (count _this > 3) then {_types = _this select 3;};
	if (typeName _types != "ARRAY") then {_types = ["Man","Car","Motorcycle","Tank"]};
	if (count _types <= 0) then  {_types = ["Man","Car","Motorcycle","Tank"]};

	if (count _this > 4) then {_nbr_grps = _this select 4;};
	if (_nbr_grps <= 0) then { _nbr_grps = 1 ;};

	if (typeName (_position select 0) == "ARRAY") then {
		_zone = _position select 1;
		_position = _position select 0;
	};

	if (typeName _side == "STRING") then {
		_side = switch {toUpper(_side)} do {
			case "WEST":{west};
			case "EAST":{east};
			case "GUER":{resistance};
			case "CIV":{civilian};
			default {d_side_enemy};
		};
	};
	
	_entities = _position nearEntities [_types, _radius];
	_grps = [];
	if ((_side countSide _entities) > 0) then {
		{
			if (side _x == _side) then {
				_grp = group _x;
				if (!(_grp in _grps)) then {
					_wp = [_grp, (currentWaypoint _grp)];
					if ((waypointType _wp) in ["MOVE","SAD","DESTROY"]) then {
						_wp setWaypointType "SAD";
						_wp setWaypointSpeed "FULL";
						_wp setWaypointPosition [_position,_zone];
						_grp setBehaviour "COMBAT";
						_grp setCurrentWaypoint _wp;
						_grps set [count _grps, _grp];
						sleep 0.5;
					};
				};
			};
			if (count _grps >= _nbr_grps) exitwith{};
		} foreach _entities;
	};
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 - - %5",diag_tickTime,"mission_functions.sqf","XfMissionConvergeUnits",_this,[(count _entities), _grps]];};
	(count _grps);
};


/// @FUNCTION
/// @NAME XfMissionGetRandomTown
/// @DESC Returns a random town where you can spawn your mission
/// @ACCESS Public
/// @MP True
/// @PARAM Boolean Include Villages @DEFAULT False
/// @PARAM Scalar The minimum size the town needs to have @DEFAULT 0
/// @PARAM Array The sectors in which you can find a town @DEFAULT [] All
/// @RETURN MapLocation The found map location
/// @REMARK
/// @EXAMPLE [False,150,[]] call XfMissionGetRandomTown
XfMissionGetRandomTown = {
	private ["_include_villages","_minimum_size","_include_sectors""_return","_parameters","_random_town","_random_town_pos"];
	_return = [];
	_include_villages = false;
	_minimum_size = 0;
	_include_sectors = [];
	if (typeName _this == "ARRAY") then {
		if (count _this > 0) then { _include_villages = _this select 0; };
		if (count _this > 1) then { _minimum_size = _this select 1; };
		if (count _this > 2) then { _include_sectors = _this select 2; };
	};
	_parameters = [d_map_locations];
	if (_include_villages) then {
		_parameters set [count _parameters, [XF_MAP_LOCATION_TYPE,"IN",[XF_MAP_LOCATION_TYPE_TOWN,XF_MAP_LOCATION_TYPE_VILLAGE]]];
	} else {
		_parameters set [count _parameters, [XF_MAP_LOCATION_TYPE,"==",XF_MAP_LOCATION_TYPE_TOWN]];
	};
	if (_minimum_size > 0) then {
		_parameters set [count _parameters, [XF_MAP_LOCATION_SIZE,">",_minimum_size]];
	};
	if ((count _include_sectors) > 0) then {
		_parameters set [count _parameters, [XF_MAP_LOCATION_MISSIONSECTOR,"IN",_include_sectors]];
	};
	_eligible_towns = _parameters call XfTableFilter;
	diag_log Format["(%1)[%2]: %3: %4 %5",diag_tickTime,"mission_functions.sqf","XfMissionGetRandomTown","Towns:",_eligible_towns];
	if ((count _eligible_towns) > 0) then {
		_random_town = _eligible_towns call XfRandomArrayVal;
		_random_town_pos = _random_town select XF_MAP_LOCATION_CENTER;
		_tries = 100;
		while {((_random_town_pos distance FLAG_BASE) < 600) && (_tries > 0)} do {
			_random_town = _eligible_towns call XfRandomArrayVal;
			_random_town_pos = _random_town select XF_MAP_LOCATION_CENTER;
			_tries = _tries - 1;
		};
		if (_tries > 0) then {_return = _random_town;};
	};
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionGetRandomTown",_return];};
	_return;
};

/// @FUNCTION
/// @NAME XfMissionGetTownsInRange
/// @DESC Returns a list of towns around a given location
/// @ACCESS Public
/// @MP True
/// @PARAM Position The position around which the towns have to be
/// @PARAM Scalar The max distance to look for. Can be an interval in the form [min,max] @DEFAULT 700
/// @PARAM Boolean Include Villages @DEFAULT False
/// @PARAM Scalar The minimum size the towns need to have @DEFAULT 0
/// @PARAM Array The sectors in which you can find a town @DEFAULT [] All
/// @RETURN Array MapLocation array values
/// @REMARK
/// @EXAMPLE [[1234,2345,0]] call XfMissionGetTownsInRange
/// @EXAMPLE [[1234,2345,0],400] call XfMissionGetTownsInRange
/// @EXAMPLE [[1234,2345,0],600,False,150,[]] call XfMissionGetTownsInRange
XfMissionGetTownsInRange = {
	private ["_center_position","_include_villages","_minimum_size","_include_sectors""_return","_parameters","_random_town","_random_town_pos"];
	_return = [];
	_include_villages = false;
	_minimum_size = 0;
	_include_sectors = [];
	_max_distance = 700;
	_min_distance = 10;
	_center_pos = _this select 0;
	if (typeName _this == "ARRAY") then {
		if (count _this > 1) then {
			_max_distance = _this select 1;
			if (typeName _max_distance == "ARRAY") then {
				_min_distance = _max_distance select 0;
				_max_distance = _max_distance select 1;
			};
		};
		if (count _this > 2) then { _include_villages = _this select 2; };
		if (count _this > 3) then { _minimum_size = _this select 3; };
		if (count _this > 4) then { _include_sectors = _this select 4; };
	};
	_parameters = [d_map_locations];
	if (_include_villages) then {
		_parameters set [count _parameters, [XF_MAP_LOCATION_TYPE,"IN",[XF_MAP_LOCATION_TYPE_TOWN,XF_MAP_LOCATION_TYPE_VILLAGE]]];
	} else {
		_parameters set [count _parameters, [XF_MAP_LOCATION_TYPE,"==",XF_MAP_LOCATION_TYPE_TOWN]];
	};
	if (_minimum_size > 0) then {
		_parameters set [count _parameters, [XF_MAP_LOCATION_SIZE,">",_minimum_size]];
	};
	if ((count _include_sectors) > 0) then {
		_parameters set [count _parameters, [XF_MAP_LOCATION_MISSIONSECTOR,"IN",_include_sectors]];
	};
	_eligible_towns = _parameters call XfTableFilter;
	diag_log Format["(%1)[%2]: %3: %4 %5",diag_tickTime,"chapter1.sqf","XfMissionChapterSetup","Towns:",_eligible_towns];
	if ((count _eligible_towns) > 0) then {
		{
			_town_pos = _x select XF_MAP_LOCATION_CENTER;
			_town_size = _x select XF_MAP_LOCATION_SIZE;
			_distance = (_town_pos distance _center_pos);
			if ((_distance <= _max_distance) && (_distance > _min_distance)
				 && (_distance > _town_size) && ((_town_pos distance FLAG_BASE) < 600)) then {
				_return set [ count _return, _x];
			};
		} foreach _eligible_towns;
	};
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionGetTownsInRange",_return];};
	_return;
};

/// @FUNCTION
/// @NAME XfMissionGetRandomSpot
/// @DESC Returns a random town where you can spawn your mission
/// @ACCESS Public
/// @MP True
/// @PARAM Scalar Type of location (see MapLocationType) Or an array of values @DEFAULT -1 All
/// @PARAM Scalar The minimum size the location needs to have @DEFAULT 0
/// @PARAM Array The sectors in which you can find the location @DEFAULT [] All
/// @PARAM Array Extra parameters that you want to use for filtering @DEFAULT [] None
/// @RETURN MapLocation The found map location
/// @REMARK
/// @EXAMPLE [XF_MAP_LOCATION_TYPE_MILITARY,150] call XfMissionGetRandomSpot
/// @EXAMPLE [[XF_MAP_LOCATION_TYPE_MILITARY,XF_MAP_LOCATION_TYPE_MOUNTAIN],150] call XfMissionGetRandomSpot
XfMissionGetRandomSpot = {
	private ["_spot_type","_minimum_size","_include_sectors""_return","_parameters","_random_town","_random_town_pos"];
	_return = [];
	_spot_type = -1;
	_minimum_size = 0;
	_include_sectors = [];
	_extra_parameters = [];
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionGetRandomSpot",_this];};
	_parameters = [d_map_locations];
	if (typeName _this == "ARRAY") then {
		if (count _this > 1) then { _minimum_size = _this select 1; };
		if (count _this > 2) then { _include_sectors = _this select 2; };
		if (count _this > 3) then { _extra_parameters = _this select 3; };
		if (count _this > 0) then {
			_spot_type = _this select 0;
			if (typename _spot_type == "ARRAY") then {
				if (count _spot_type > 0) then {
					_parameters set [count _parameters, [XF_MAP_LOCATION_TYPE,"IN",_spot_type]];
				};
			} else {
				if (_spot_type >= 0) then {
					_parameters set [count _parameters, [XF_MAP_LOCATION_TYPE,"==",_spot_type]];
				};
			};
		};
	};
	if (_minimum_size > 0) then {
		_parameters set [count _parameters, [XF_MAP_LOCATION_SIZE,">",_minimum_size]];
	};
	if ((count _include_sectors) > 0) then {
		_parameters set [count _parameters, [XF_MAP_LOCATION_MISSIONSECTOR,"IN",_include_sectors]];
	};
	if ((count _extra_parameters) > 0) then {
		{
			_parameters set [count _parameters, _x];
		} foreach _extra_parameters;
	};
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionGetRandomSpot",_parameters];};
	_eligible_towns = _parameters call XfTableFilter;
	// diag_log Format["(%1)[%2]: %3: %4 %5",diag_tickTime,"mission_functions.sqf","XfMissionGetRandomSpot","Towns:",_eligible_towns];
	if ((count _eligible_towns) > 0) then {
		_random_town = _eligible_towns call XfRandomArrayVal;
		_random_town_pos = _random_town select XF_MAP_LOCATION_CENTER;
		_tries = 100;
		while {((_random_town_pos distance FLAG_BASE) < 600) && (_tries > 0)} do {
			_random_town = _eligible_towns call XfRandomArrayVal;
			_random_town_pos = _random_town select XF_MAP_LOCATION_CENTER;
			_tries = _tries - 1;
		};
		if (_tries > 0) then {_return = _random_town;};
	};
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionGetRandomSpot",_return];};
	_return;
};

/// @FUNCTION
/// @NAME XfMissionGetCivilianType
/// @DESC Returns a civilian type corresponding to the given criterias
/// @ACCESS Server
/// @MP False
/// @PARAM String The side of the civilian to create
/// @RETURN String Civilian type
/// @REMARK
/// @EXAMPLE ["EAST"] call XfMissionGetCivilianType
XfMissionGetCivilianType = {
	private ["_side","_side_char","_units_list", "_unit"];
	_side = _this select 0;
	
	if (typename _side == typename east) then {
		_side = switch (_side) do {case east: {"EAST"};case west: {"WEST"};case resistance: {"GUER"}; default{"CIV"};};
	};
	_side_char = switch (_side) do {case "EAST": {"E"};case "WEST": {"W"};case "GUER": {"G"};case "CIV": {"E"}; default{"E"};};
	
	_units_list = missionNamespace getVariable format ["d_civilian_%1",_side_char];
	
	_unit = _units_list call XfRandomArrayVal;
	
	_unit;
};

/// @FUNCTION
/// @NAME XfMissionGetCharacter
/// @DESC Returns a character type to create a unit for a mission
/// @ACCESS Server
/// @MP False
/// @PARAM String The side of the civilian to create
/// @PARAM Array List of character types to take into account (numeric values, use the constants if possible)
/// @RETURN String Character type
/// @REMARK
/// @EXAMPLE ["EAST",[XF_MISSION_CHAR_CIVILIAN_WOMEN]] call XfMissionGetCharacter
XfMissionGetCharacter = {
	private ["_side","_side_char","_units_list", "_unit"];
	_side = _this select 0;
	_filter = _this select 1;
	_unit = "";
	if (typename _side == typename east) then {
		_side = switch (_side) do {case east: {"EAST"};case west: {"WEST"};case resistance: {"GUER"}; default{"CIV"};};
	};
	_side_char = switch (_side) do {case "EAST": {"E"};case "WEST": {"W"};case "GUER": {"G"};case "CIV": {"E"}; default{"E"};};
	
	_units_list = missionNamespace getVariable format ["d_special_mission_characters_%1",_side_char];
	
	if (count _filter > 0) then {
		_merged_list = [];
		{
			_merged_list = [_merged_list, (_units_list select _x)] call XfArrayPushStack;
		} foreach _filter;
		_unit = _merged_list call XfRandomArrayVal;
	};
	_unit;
};

/// @FUNCTION
/// @NAME XfMissionCivilianSpawn
/// @DESC Generates civilian units from a passed array of types and generate movements for them in an apart thread. They form one group, so are managed separately through a loop rather than through a patrol script.
/// @ACCESS Server
/// @MP False
/// @PARAM Position The center of the area to cover. You can give it with a radius to override the basic radius of 100.
/// @PARAM String The side of the civilian units to spawn.
/// @PARAM Scalar The max amount of civilian. It ends up always being a number between this amount and half this amount. @DEFAULT 10
/// @PARAM Array The array containing the different civilian units we want to spawn. @DEFAULT d_civilian_X (Give an empty array for default)
/// @PARAM Scalar Score lost when killing a civilian @DEFAULT 20
/// @RETURN Nothing
/// @REMARK
/// @EXAMPLE [[d_town_pos,200],"EAST",10] call XfMissionCivilianSpawn
XfMissionCivilianSpawn = {
	private ["_center","_radius","_side","_side_char","_units_list","_units","_unit","_minimum","_maximum","_amount","_grp","_wps","_buildings_pos","_civilian_kill","_mrk_name"];
	_center = _this select 0;
	_side = _this select 1;
	_radius = 100;
	_side = switch (_side) do {case "EAST": {_side};case "WEST": {_side};case "GUER": {_side};case "CIV": {_side}; default{d_enemy_side};};
	_side_char = switch (_side) do {case "EAST": {"E"};case "WEST": {"W"};case "GUER": {"G"};case "CIV": {"E"}; default{"E"};};
	_minimum = 5;
	_maximum = 10;
	_amount = 7;
	if (count _this > 2) then { _maximum = round(_this select 2); };
	if (_maximum < 2) then {_maximum = 2};
	_minimum = round(_maximum / 2);
	_amount = _minimum + round(random(_maximum - _minimum));
	_units_list = missionNamespace getVariable format ["d_civilian_%1",_side_char];
	if (count _this > 3) then { _units_list = _this select 3 };
	if (count _units_list == 0) then {_units_list = missionNamespace getVariable format ["d_civilian_%1",_side_char];};
	_civilian_kill = 20;
	if (count _this > 4) then { _civilian_kill = _this select 4 };

	if (typename (_center select 0) == "ARRAY") then {
		_radius = _center select 1;
		_center = _center select 0;
	};
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 %5 %6 (%7)",diag_tickTime,"mission_functions.sqf","XfMissionCivilianSpawn",_this, [_amount, _radius,_side_char],_units_list,DM_Mission_KamikazeCivilian];};

	_units = [];
	if (_amount > 0 and count _units_list > 0) then {
		For [{_i = 0},{_i < _amount},{_i = _i + 1}] do {
			_unit = _units_list call XfRandomArrayVal;
			_units set [count _units, _unit];
		};
	};
	
	if (count _units > 0) then {
		// We create the group itself in a small radius so it spawns in town.
		_grp = [_units,true,_side,[_center,(_radius / 3)],[],false,"","NONE",false] call XfMissionCreateUnitGroup;
		_grp setBehaviour "SAFE";
		_grp setSpeedMode "LIMITED";
		_grp setCombatMode "BLUE";
		_grp allowFleeing 1;
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 %5",diag_tickTime,"mission_functions.sqf","XfMissionCivilianSpawn",_grp,units _grp];};
		// We will precalculate enough random waypoints for civilians in order to make the movement thread much lighter.
		_wps = [];
		// We will have 2,5 times more Waypoints than civilians. With a limit of 50.
		_amount = _amount * 2.5;
		if (_amount > 50) then {_amount = 50;};
		// We want at least as many WPs as units.
		if (_amount < count _units) then { _amount = count _units;};
		_buildings_pos = [_center,_radius,"INSIDE"] call XfMissionBuildingAreaPosASL;
		For [{_i = 0},{_i < _amount},{_i = _i + 1}] do {
			// We give a 20% chance to have the pos inside a building.
			if ((random(10) >= 8) && (count _buildings_pos > 0)) then {
				_wps set [count _wps, (_buildings_pos call XfRandomArrayVal)];
			} else {
				_wps set [count _wps, ([_center,_radius] call XfGetRanPointCircle)];
			};
			if (d_mission_debug_messages) then {
				_mrk_name = Format["wp%1%2%3",(_wps select _i) select 0,(_wps select _i) select 1,round(random(100))];
				createMarkerLocal [_mrk_name, (_wps select _i)];
				_mrk_name setMarkerAlphaLocal 0.5;
				_mrk_name setMarkerShapeLocal "ICON";
				_mrk_name setMarkerColorLocal "ColorYellow";
				_mrk_name setMarkerTypeLocal "mil_triangle";
				_mrk_name setMarkerSizeLocal [0.5, 0.5];
			};
		};
		if (DM_Mission_KamikazeCivilian) then {
			_civilians = units _grp;
			_kamikaze = _civilians select (floor (random (count _civilians)));
			[d_side_player,_kamikaze,80,"R_OG9_HE",0.7] spawn XfMissionKamikaze;
			_kamikaze setVariable ["IsKamikaze",true, true];
			if (d_mission_debug_messages) then {
				diag_log Format["(%1)[%2]: %3: %4 %5",diag_tickTime,"mission_functions.sqf","XfMissionCivilianSpawn","Kamikaze:",typeof _kamikaze];
				[_kamikaze, "Kamikaze"] spawn XfAirMarkerMove;
			};
		};
		
		if (count _wps > 0 && count (units _grp) > 0) then {
			if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: grp:%4  wps:%5",diag_tickTime,"mission_functions.sqf","XfMissionCivilianSpawn",_grp,count _wps];};
			{
				_x addEventHandler ["killed",Format["%1%2",Format["_killed = _this select 0; _killer = _this select 1; _killer addScore (score _killer - %1);",_civilian_kill], "_killed removeAllEventHandlers ""killed""; if(isPlayer _killer) then {Format[""%1 killed a civilian!"",name(_killer)] call XfMissionMessageToAll;};"]];
				_x setPos ([_center,_radius] call XfGetRanPointCircle);
				if (d_mission_debug_messages) then {
					_mrk_name = Format["civilian_%1%2%3",(getPos _x) select 0,(getPos _x) select 1,round(random(100))];
					createMarkerLocal [_mrk_name, getPos _x];
					_mrk_name setMarkerAlphaLocal 0.6;
					_mrk_name setMarkerColorLocal "ColorGreen";
					_mrk_name setMarkerShapeLocal "ICON";
					_mrk_name setMarkerTypeLocal "hd_dot";
					_mrk_name setMarkerSizeLocal [0.7, 0.7];
					_x setVariable ["marker",_mrk_name];
				};
			} foreach (units _grp);
			[_grp, _wps] spawn {
				private ["_group","_waypoints","_pos","_mrk_name"];
				_group = _this select 0;
				_group setBehaviour "SAFE";
				_group setSpeedMode "LIMITED";
				_waypoints = _this select 1;
				sleep 30;
				while {!isnull _group && alive (leader _group) } do {
					{
						// There are chances we don't move.
						if (random (100) >= 40) then {
							_b = behaviour _x;
							switch { _b } do {
								case "AWARE": { };
								case "COMBAT": { };
								case "STEALTH": { };
								default {
									_pos = (_waypoints call XfRandomArrayVal);
									_x doMove _pos;
									_x setSpeedMode "LIMITED";
								};
							};
						};
						if (d_mission_debug_messages) then {
							_mrk_name = _x getVariable "marker";
							if (!isnil "_mrk_name") then {	_mrk_name setMarkerPosLocal _pos;};
						};
					} foreach (units _group);
					sleep (30 + random 20);
				};			
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 %5",diag_tickTime,"mission_functions.sqf","XfMissionCivilianSpawn",_grp,"Group Exited"];};
			};
		};
	};
	
};

/// @FUNCTION
/// @NAME XfMissionStuckUnit
/// @DESC Generates civilian units from a passed array of types and generate movements for them in an apart thread. They form one group, so are managed separately through a loop rather than through a patrol script.
/// @ACCESS Server
/// @MP False
/// @PARAM Position The center of the area to cover. You can give it with a radius to override the basic radius of 100.
/// @PARAM String The side of the civilian units to spawn.
/// @PARAM Scalar The max amount of civilian. It ends up always being a number between this amount and half this amount. @DEFAULT 10
/// @PARAM Array The array containing the different civilian units we want to spawn. @DEFAULT d_civilian_X (Give an empty array for default)
/// @PARAM Scalar Score lost when killing a civilian @DEFAULT 20
/// @RETURN Nothing
/// @REMARK
/// @EXAMPLE [_unit] call XfMissionStuckUnit
XfMissionStuckUnit = {
	if (typename _this != typename []) exitWith {};
	if (count _this <= 0) exitWith {};
	_unit = _this select 0;
	_unit DisableAI "MOVE";
	if (!isnull _unit) then {
		_this spawn {
			_unit = _this select 0;
			_initial_pos = getPosASL _unit;
			_chance_move = 0;
			while { alive _unit } do {
				sleep 0.12345;
				_targets = _unit nearTargets 400;
				if (count _targets > 0) then {
					_nearest_enemy_pos = [];
					_nearest_target = [];
					_nearest_enemy_dist = 1000;
					{
						// [[2555.33,2535.33,1.32708],"SoldierEB",EAST,214222,EAST 1-1-A:1]
						_perceived_side = (_x select 2);
						if (_perceived_side == d_side_player
							|| _perceived_side == resistance) then {
							_perceived_pos = (_x select 0);
							_dist = _perceived_pos distance _unit;
							if (_dist < _nearest_enemy_dist) then { _nearest_enemy_pos = _perceived_pos; _nearest_target = _x;};
						};
					} foreach _targets;
					if (count _nearest_enemy_pos > 0) then {
						if (d_mission_debug_messages) then { 
							diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","_fnc_sniper_spotting (spotted)",_nearest_target];
						};
						_start_dir = floor(getDir _unit);
						_end_dir = floor([getPos _unit, _nearest_enemy_pos] call XfDirTo);
						if (_end_dir < 0) then {_end_dir = 360 + _end_dir; };
						_rotation = floor(_end_dir - _start_dir);
						_incr = 1;
						if (_rotation < 0) then {
							_incr = -1;
						};
						if (_rotation >= 180) then {
							_rotation = 360 - _rotation;
							_incr = -1;
						};
						// [[13059.7,16496.3,0.820672],59.6898,168,79,-89,79,-1]
						if (d_mission_debug_messages) then { 
							diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","_fnc_sniper_spotting (turning)",[_nearest_enemy_pos, (_nearest_enemy_pos distance _unit), _start_dir, _end_dir, _rotation, (_start_dir + _rotation), _incr]];
						};
						for [{_i = _start_dir}, {abs(abs(_end_dir) - abs(floor(getDir _unit))) > 2}, { _i = _i + _incr}] do {
							if (!alive _unit) exitWith{};
							diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","_fnc_sniper_spotting (turn)",[floor(getDir _unit), _end_dir, _i, _incr]];
							_unit setDir _i;
							sleep 0.003;
						};
					} else {
						_chance_move = _chance_move + 1;
						if (_chance_move > 100) then {
							_chance_move = 0;
							if (random(10) > 7) then {
								_start_dir = floor(getDir _unit);
								_end_dir = floor(_start_dir + random(180));
								for [{_i = _start_dir}, {abs(abs(_i) - abs(_end_dir)) <= 1}, { _i = _i + 1}] do {
									_unit setDir _i;
									sleep 0.01;
								};
							};
						};
					};
				};
			};
		};
	};
};
	
	
// ************* DISCONNECT *************
// This function is called when a players disconnects to drop his objects and such.
XfMissionPlayerDisconnected = {
	private ["_player_name","_player_id","_player_uid","_player_pos", "_player_list"];
	_player_name = _this select 0;
	_player_id = _this select 0;
	_player_uid = _this select 0;
	_player = "";
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionPlayerDisconnected",_this];};

	_player_list = d_player_store getVariable "player_positions";
	if (isNil "_player_list") then {_player_list = []};
	_indexer = [];
	_names = [];
	_positions =[];
	_index = -1;
	// TODO: Implement an alternative where we check all logged in players and look in picked up objects
	//       If a player in picked up objects is not present in the logged in list, then all his stuff is dropped.
	if (count _player_list > 2) then {
		_indexer = _player_list select 0;
		_positions = _player_list select 1;
		_names = _player_list select 2;
		_index = _names find _player_name;
		if (_index >= 0) then {
			_player = _indexer select _index;
		};
		diag_log Format["(%1)[%2]: %3: %4: %5, %6",diag_tickTime,"mission_functions.sqf","XfMissionPlayerDisconnected","Player list",_index,_player];
	};
	_player_pos = [];
	if (_player != "") then { _player_pos = getPosATL (missionNamespace getVariable _player); };
	if (isNil "_player_pos") then {_player_pos =[]};
	if (count _player_pos > 0) then { if (surfaceIsWater[(_player_pos select 0),(_player_pos select 1)]) then {_player_pos =[]}; };

	If ((count _player_pos < 3) OR (Format["%1",_player_pos] == "[0,0,0]")) then {
		// The player pos is not to be found anymore
		// We need to take the last one the server had
		if (_index >= 0) then {
			_player_pos = _positions select _index;
			if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4: %5",diag_tickTime,"mission_functions.sqf","XfMissionPlayerDisconnected","Found position", _player_pos];};
		};
	} else {
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4: %5",diag_tickTime,"mission_functions.sqf","XfMissionPlayerDisconnected","Player position", _player_pos];};
	};
	If ((count _player_pos < 3) OR (Format["%1",_player_pos] == "[0,0,0]")) then {
		// In case all attempts failed to find the player, we spawn everything at the base flag
		_player_pos = getMarkerPos "base_spawn_1";
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4: %5",diag_tickTime,"mission_functions.sqf","XfMissionPlayerDisconnected","Flag position", _player_pos];};
	};

	if (_player != "") then {
		_player_objects = [];
		_array_index = -1;
		_forindex = 0;
		_pickedup_objects = ["mission_pickedup_objects",[]] call XNetGetJIP;
		if (count _pickedup_objects > 0) then {
			{
				diag_log Format["(%1)[%2]: %3: %4: %5",diag_tickTime,"mission_functions.sqf","XfMissionPlayerDisconnected","     Objects",_x];
				if ((_x select 0) == _player) exitWith {
					_player_objects = _x select 1;
					_array_index = _forindex;
				};
				_forindex = _forindex + 1;
			} foreach _pickedup_objects;
		};
	};
	diag_log Format["(%1)[%2]: %3: %4: %5",diag_tickTime,"mission_functions.sqf","XfMissionPlayerDisconnected","Player Objects",_player_objects];
	if (_array_index >= 0) then {
		if (isNil "_player_objects") then {_player_objects = []};
		If (count _player_objects > 0) then {
			_forindex = 0;
			[false,[format["marker_drop_disconnect_%1",_player], _player_pos, "ICON", "ColorRed", [0.7,0.7],Format["%1's Mission Objects",_player_name],0,"hd_warning","",1]] call XfMissionMarker;
			{
				_object_array = _x;
				if (!isNil "_object_array") then {
					_item_desc = _object_array select 0;
					_obj_type = _object_array select 1;
					_action = _object_array select 2;
					_parameters = _object_array select 3;
					_creation_pos = _player_pos findEmptyPosition [0,50,_obj_type];
					if (count _creation_pos > 0) then {
						_new_object = createVehicle [_obj_type,_creation_pos,[],1,"NONE"];
						if (!isNull _new_object) then {
							[_new_object,_action] call XfMissionSetAction;
							[_new_object,_parameters] call XfMissionSetParameters;
							if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 - %5(%6)",diag_tickTime,"mission_functions.sqf","Registering the object",(str _new_object),_action,count _parameters];};
							_new_object call XfMissionAddObject;
							["d_all_mobject",_new_object] call XNetCallEvent;
							["d_msg",[Format["%1 has dropped %2",_player_name,_item_desc],"all",objNull,"hq"]] call XNetCallEvent;
							player removeAction _action_id;
						};
					} else {
						"No suitable position to drop the item!" call XfGlobalChat;
					};
				};
				_player_objects set [_forindex, "xXxDELxXx"];
				_forindex = _forindex + 1;
				diag_log Format["(%1)[%2]: %3: %4: %5",diag_tickTime,"mission_functions.sqf","XfMissionPlayerDisconnected","    Object ",_x];
			} foreach _player_objects;
			_player_objects = _player_objects - ["xXxDELxXx"];
		};
		// We update the array with the new player object list.
		_pickedup_objects set [_array_index,[_player,_player_objects]];
		["mission_pickedup_objects",_pickedup_objects] call XNetSetJIP;
	} else {
		// The player has no object nothing to do
		diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionPlayerDisconnected","The player has no objects listed"];
	};
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: Picked up objects:%4",diag_tickTime,"mission_functions.sqf","XfMissionPlayerDisconnected",(["mission_pickedup_objects",[]] call XNetGetJIP)];};
};

// ===========================================================================
//   						SERVER SIDE FUNCTIONS
// ===========================================================================


// ************* DESTROY *************
// Object needs to be destroyed by the player.
// On the server side we create the damage handler.
XfMissionObjectServerSetupDestroy = {
	private ["_object","_action","_parameters","_destroyed_msg","_shared_variable","_shared_variable_value","_ammo_amount","_ammo_types","_destroyed_code"];
	_object = _this select 0;
	_action = _this select 1;
	_parameters = _this select 2;
	
	// _destroyed_msg = _parameters select 0;
	// _shared_variable = _parameters select 1;
	// _shared_variable_value = True;
	// if (count _parameters > 2) then {_shared_variable_value = _parameters select 2;};
	// _ammo_amount = 2;
	// if (count _parameters > 3) then {_ammo_amount = _parameters select 3;};
	// _ammo_types = ["PipeBomb"];
	// if (count _parameters > 4) then {if (count (_parameters select 4) > 0) then {_ammo_types = _parameters select 4;};};
	// _destroyed_code = "";
	// if (count _parameters > 5) then {_destroyed_code = _parameters select 5;};
	
	_object addEventHandler ["HandleDamage",{_this call XfMissionEventObjectiveDestroy;}];
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: Damage Handler added",diag_tickTime,"mission_functions.sqf","XfMissionObjectServerSetupDestroy"];};
};

// ************* PICKUP *************
// Object has to be picked up by the player
// We make the object indestructible
XfMissionObjectServerSetupPickup = {
	private ["_object","_action","_parameters","_item_desc","_action_title","_pickup_msg","_shared_variable","_shared_variable_value","_pickup_code"];
	_object = _this select 0;
	_action = _this select 1;
	_parameters = _this select 2;
	
	// _item_desc = _parameters select 0;
	// _pickup_msg = _parameters select 1;
	// _shared_variable = _parameters select 2;
	// _action_title = "Pick Up";
	// if (count _parameters > 3) then {_action_title = _parameters select 3;};
	// _shared_variable_value = True;
	// if (count _parameters > 4) then {_shared_variable_value = _parameters select 4;};
	// _pickup_code = "";
	// if (count _parameters > 5) then {_pickup_code = _parameters select 5;};
	// if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: obj:%4  desc:%5  msg:%6  var:%7  val:%8  title:%9  code:%10",diag_tickTime,"mission_functions.sqf","XfMissionObjectServerSetupPickup",typeof _object,_item_desc, _pickup_msg, _shared_variable, _shared_variable_value, _action_title, (_pickup_code != "")];};

	_object allowDamage false;
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: AllowDamage false",diag_tickTime,"mission_functions.sqf","XfMissionObjectServerSetupPickup"];};
};
// When the object is picked up by the player, we delete it, set the global var and finally send a message
XfMissionObjectServerExecutePickup = {
	private ["_object","_action","_parameters","_item_desc","_action_title","_pickup_msg","_shared_variable","_shared_variable_value","_pickup_code"];
	diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionObjectServerExecutePickup",_this];

	_object = _this select 0;
	_action = _this select 1;
	_parameters = _this select 2;
	_player = _this select 3;
	_action_id = _this select 4;
	
	_item_desc = _parameters select 0;
	_pickup_msg = _parameters select 1;
	_shared_variable = _parameters select 2;
	_action_title = "Pick Up";
	if (count _parameters > 3) then {_action_title = _parameters select 3;};
	_shared_variable_value = True;
	if (count _parameters > 4) then {_shared_variable_value = _parameters select 4;};
	_pickup_code = "";
	if (count _parameters > 5) then {_pickup_code = _parameters select 5;};
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: obj:%4  desc:%5  msg:%6  var:%7  val:%8  title:%9  code:%10  player:%11",diag_tickTime,"mission_functions.sqf","XfMissionObjectServerExecutePickup",typeof _object,_item_desc, _pickup_msg, _shared_variable, _shared_variable_value, _action_title, (_pickup_code != ""),str(_player)];};
	If (_pickup_msg != "") then {["d_msg",[_pickup_msg,"all",objNull,"hq"]] call XNetCallEvent; };
	_object allowDamage true;
	if (!isNil "_shared_variable") then {
		if (_shared_variable != "") then {[_shared_variable,_shared_variable_value] call XNetSetJIP;};
	};
	deleteVehicle _object;
	if (_pickup_code != "") then {
		["d_all_exec",[_pickup_code,_object]] call XNetCallEvent;
	};
};
// After the object has been picked up, it can be put down aswell (to allow transfer to another player)
XfMissionObjectServerExecuteDrop = {
	// Here we remove the items from the list of the player owned objects
	private ["_object","_action","_parameters","_player","_action_id","_arguments","_item_desc","_object_array","_pickedup_item","_obj_type"];
	_object = _this select 0; // Contains the player in this particular case, since he possesses the object
	_action = _this select 1;
	_parameters = _this select 2;
	_player = _this select 3;
	_action_id = _this select 4;
	_arguments = _this select 5; // Contain the action name and the reference of the object in the array
};

// ************* ACTIVATE *************
// Object can be activated/deactivated by the player
// We make the object indestructible
XfMissionObjectServerSetupActivate = {
	private ["_object","_action","_parameters","_activated_msg","_deactivated_msg","_shared_variable","_initial_status","_activated_title","_deactivated_title","_activated_code","_deactivated_code"];
	_object = _this select 0;
	_action = _this select 1;
	_parameters = _this select 2;
	
	// _activated_msg = _parameters select 0;
	// _deactivated_msg = _parameters select 1;
	_shared_variable = _parameters select 2;
	// _activated_title = "Deactivate";
	// if (count _parameters > 3) then {_activated_title = _parameters select 3;};
	// _deactivated_title = "Activate";
	// if (count _parameters > 4) then {_deactivated_title = _parameters select 4;};
	_initial_status = False;
	if (count _parameters > 5) then {_initial_status = _parameters select 5;};
	// _activated_code = "";
	// if (count _parameters > 6) then {_activated_code = _parameters select 6;};
	// _deactivated_code = "";
	// if (count _parameters > 7) then {_deactivated_code = _parameters select 7;};
	if (!isNil "_shared_variable") then {
		if (_shared_variable != "") then {[_shared_variable,_initial_status] call XNetSetJIP;};
	};
	
	_object setVariable ["mission_object_activation_status",_initial_status, true];
	_object allowDamage false;
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: AllowDamage false",diag_tickTime,"mission_functions.sqf","XfMissionObjectServerSetupActivate"];};
};
XfMissionObjectServerExecuteActivate = {
	private ["_object","_action","_parameters","_player","_action_id","_current_status","_activated_msg","_deactivated_msg","_shared_variable","_initial_status","_activated_title","_deactivated_title","_activated_code","_deactivated_code"];
	_object = _this select 0;
	_action = _this select 1;
	_parameters = _this select 2;
	_player = _this select 3;
	_action_id = _this select 4;
	
	_activated_msg = _parameters select 0;
	_deactivated_msg = _parameters select 1;
	_shared_variable = _parameters select 2;
	_activated_title = "Deactivate";
	if (count _parameters > 3) then {_activated_title = _parameters select 3;};
	_deactivated_title = "Activate";
	if (count _parameters > 4) then {_deactivated_title = _parameters select 4;};
	_initial_status = False;
	if (count _parameters > 5) then {_initial_status = _parameters select 5;};
	_activated_code = "";
	if (count _parameters > 6) then {_activated_code = _parameters select 6;};
	_deactivated_code = "";
	if (count _parameters > 7) then {_deactivated_code = _parameters select 7;};
	
	_current_status = _object getVariable "mission_object_activation_status";
	If (isNil "_current_status") then {_current_status = _initial_status};
	_current_status = !_current_status;
	_object setVariable ["mission_object_activation_status",_current_status, true];
	// We change the initial variable for the JIP players
	_parameters set [3,_activated_title];
	_parameters set [4,_deactivated_title];
	_parameters set [5,_current_status];
	[_object,_parameters] call XfMissionSetParameters;
	
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: obj:%4  actm:%5 deactm:%6  var:%7  ini:%8  actt:%9  deactt:%10  sts:%11  actc:%12  deactc:%13",diag_tickTime,"mission_functions.sqf","XfMissionObjectServerExecuteActivate",typeof _object, _activated_msg, _deactivated_msg, _shared_variable, _initial_status, _activated_title, _deactivated_title, _current_status, (_activated_code != ""),(_deactivated_code != "")];};
	if (!isNil "_shared_variable") then {
		if (_shared_variable != "") then {[_shared_variable,_current_status] call XNetSetJIP;};
	};
	if (_current_status) then {
		if (_activated_msg != "") then {["d_msg",[_activated_msg,"all",objNull,"hq"]] call XNetCallEvent; };
		if (_activated_code != "") then { ["d_all_exec",[_activated_code,_object]] call XNetCallEvent; };
	} else {
		if (_deactivated_msg != "") then {["d_msg",[_deactivated_msg,"all",objNull,"hq"]] call XNetCallEvent; };
		if (_deactivated_code != "") then {	["d_all_exec",[_deactivated_code,_object]] call XNetCallEvent; };
	};
};

// ************* ASSASSINATE *************
// Object can be activated/deactivated by the player
// We make the object indestructible
XfMissionObjectServerSetupAssassinate = {
	private ["_object","_action","_parameters","_killed_msg","_shared_variable","_killed_code","_evt_idx"];
	_object = _this select 0;
	_action = _this select 1;
	_parameters = _this select 2;
	
	if ((_object isKindOf "Man") Or (_object isKindOf "LandVehicle") Or (_object isKindOf "Air") Or (_object isKindOf "Ship")) then {
		_killed_msg = _parameters select 0;
		_shared_variable = _parameters select 1;
		_killed_code = "";
		if (count _parameters > 2) then {_killed_code = _parameters select 2;};
		_evt_idx = _object addEventHandler ["killed", { _this call XfMissionEventObjectiveKilled}];
		_object setVariable ["mission_killed_evt",_evt_idx];
		if (!isNil "_shared_variable") then {
			if (_shared_variable != "") then {[_shared_variable,false] call XNetSetJIP;};
		};
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: Killed handler added",diag_tickTime,"mission_functions.sqf","XfMissionObjectServerSetupAssassinate"];};
	} else {
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 %5",diag_tickTime,"mission_functions.sqf","XfMissionObjectServerSetupAssassinate","Is not a valid class", _object];};
	};
};

// ************* CONQUER *************
// The area needs to be cleared of all enemies.
// On the server side we create the trigger which detects the absence of enemy units.
XfMissionObjectServerSetupConquer = {
	private ["_object","_action","_parameters","_conquer_msg","_conquer_zone","_conquer_side","_shared_variable","_shared_variable_value","_conquer_code","_conquer_zone_center","_conquer_zone_size","_conquer_zone_angle","_conquer_zone_rect","_object_identifier","_conquer_left"];
	_object = _this select 0;
	_action = _this select 1;
	_parameters = _this select 2;

	_conquer_msg = _parameters select 0;
	_conquer_zone = _parameters select 1;
	_conquer_side = toUpper(_parameters select 2);
	_shared_variable = _parameters select 3;
	_conquer_left = 3;
	if (count _parameters > 4) then {_conquer_left = _parameters select 4};
	_conquer_code = "";
	if (count _parameters > 5) then {_conquer_code = _parameters select 5;};
	_conquer_zone_center = _conquer_zone select 0;
	_conquer_zone_size = _conquer_zone select 1;
	_conquer_zone_angle = _conquer_zone select 2;
	_conquer_zone_rect = _conquer_zone select 3;
	
	_conquer_side = switch {_conquer_side} do {
		case "EAST": {"EAST"};
		case "WEST": {"WEST"};
		case "GUER": {"GUER"};
		case "CIV": {"CIV"};
		default{ d_enemy_side};	
	};
	if (!isNil "_shared_variable") then {
		if !(_shared_variable == "") then {[_shared_variable,false] call XNetSetJIP;};
	};
	_object_identifier = Format["%1_%2%3%4%5%6",typeof _object,floor(_conquer_zone_center select 0),floor(_conquer_zone_center select 1),floor(_conquer_zone_center select 2),count d_mission_object_list,floor(random(floor(diag_tickTime))) ];
	// We make this variable local only because only the server needs it.
	_object setVariable ["mission_object_identifier",_object_identifier];
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: msg:%4  zone:%5  objid:%6  var:%7  side:%8  code:%9",diag_tickTime,"mission_functions.sqf","XfMissionObjectServerSetupConquer",_conquer_msg,_conquer_zone,_object_identifier,_shared_variable,d_enemy_side,(_conquer_code != "")];};
	// Ex: [getPos Player, [100,200,33,True], ["WEST", "EAST D", true], ["this", "hint 'trigger on';", "hint 'trigger off';"]] call XfCreateTrigger
	_trigger = [_conquer_zone_center, [(_conquer_zone_size select 0),(_conquer_zone_size select 1),_conquer_zone_angle,_conquer_zone_rect], [_conquer_side, "NOT PRESENT", true], [Format["this && (count thislist <= %1)",_conquer_left], Format["[thisTrigger,'%1'] call XfMissionEventObjectiveConquer",_object_identifier], ""]] call XfCreateTrigger;
	_trigger setTriggerTimeout [d_mission_conquered_base_timeout, 1.25 * d_mission_conquered_base_timeout, 1.5 * d_mission_conquered_base_timeout, true];
	d_mission_trigger_list set [count d_mission_trigger_list, _trigger];
};

// ************* RESCUE *************
// The person has to be freed by players
// We make the person captive and spawn a script to make it sit and remain bound
XfMissionObjectServerSetupRescue = {
	private ["_object","_action","_parameters","_freed_msg","_die_msg","_shared_free","_shared_alive","_free_code","_die_code","_evt_idx"];
	_object = _this select 0;
	_action = _this select 1;
	_parameters = _this select 2;
	
	if (_object isKindOf "Man") then {
		_freed_msg = _parameters select 0;
		_die_msg = _parameters select 1;
		_shared_free = _parameters select 2;
		_shared_alive = _parameters select 3;
		_free_code = "";
		if (count _parameters > 4) then {_free_code = _parameters select 4;};
		_die_code = "";
		if (count _parameters > 5) then {_die_code = _parameters select 5;};
		
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: obj:%4  fmsg:%5  dmsg:%6  fvar:%7  avar:%8  fcode:%9  dcode:%10",diag_tickTime,"mission_functions.sqf","XfMissionObjectServerSetupRescue",typeof _object, _freed_msg, _die_msg, _shared_free, _shared_alive, (_free_code != ""), (_die_code != "")];};
		_evt_idx = _object addEventHandler ["killed", { _this call XfMissionEventObjectiveRescueFailed}];
		_object setVariable ["mission_rescue_failed_evt",_evt_idx];
		_object setCaptive true;
		_object disableAI "AUTOTARGET";
		_object disableAI "MOVE";
		
		if (!isNil "_shared_free") then {
			if (_shared_free != "") then {[_shared_free,false] call XNetSetJIP;};
		};
		if (!isNil "_shared_alive") then {
			if (_shared_alive != "") then {[_shared_alive,alive _object] call XNetSetJIP;};
		};
	};
};
// When the hostage is freed, we set the right variables up
XfMissionObjectServerExecuteRescue = {
	private ["_object","_action","_parameters","_player","_action_id","_freed_msg","_die_msg","_shared_free","_shared_alive","_free_code","_die_code","_evt_idx"];
	_object = _this select 0;
	_action = _this select 1;
	_parameters = _this select 2;
	_player = _this select 3;
	_action_id = _this select 4;
	
	_freed_msg = _parameters select 0;
	_die_msg = _parameters select 1;
	_shared_free = _parameters select 2;
	_shared_alive = _parameters select 3;
	_free_code = "";
	if (count _parameters > 4) then {_free_code = _parameters select 4;};
	_die_code = "";
	if (count _parameters > 5) then {_die_code = _parameters select 5;};
	
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: obj:%4  fmsg:%5  dmsg:%6  fvar:%7  avar:%8  fcode:%9  dcode:%10  actid:%11",diag_tickTime,"mission_functions.sqf","XfMissionObjectServerExecuteRescue",typeof _object, _freed_msg, _die_msg, _shared_free, _shared_alive, (_free_code != ""), (_die_code != ""),_action_id];};
	_object setCaptive false;
	_object enableAI "AUTOTARGET";
	_object enableAI "MOVE";
	
	
	_object allowDamage false;
	_hostage_pos = getPosASL _object;

	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: obj:%4  fmsg:%5  dmsg:%6  fvar:%7  avar:%8  fcode:%9  dcode:%10  actid:%11",diag_tickTime,"mission_functions.sqf","XfMissionObjectServerExecuteRescue",typeof _object, _freed_msg, _die_msg, _shared_free, _shared_alive, (_free_code != ""), (_die_code != ""),_action_id];};
	_object setCaptive false;
	_object enableAI "AUTOTARGET";
	_object enableAI "MOVE";
	
	if (!isNil "_freed_msg") then {
		if (_freed_msg != "") then {["d_msg",[_freed_msg,"all",objNull,"hq"]] call XNetCallEvent; };
	};
	if (!isNil "_shared_free") then {
		if (_shared_free != "") then {[_shared_free,true] call XNetSetJIP;};
	};
	if (!isNil "_shared_alive") then {
		if (_shared_alive != "") then {[_shared_alive,alive _object] call XNetSetJIP;};
	};

	_evt_idx = _object getVariable "mission_rescue_failed_evt";
	if (!isNil "_evt_idx") then {_object removeEventHandler ["killed", _evt_idx]; _object setVariable ["mission_rescue_failed_evt", nil];};
	
	if (alive _object) then {
		sleep 1;
		_object setPosASL _hostage_pos;
		sleep 5;
		_object allowDamage true;
	};
};

// ************* LEAD *************
// The person has to be led to a destination and kept alive
XfMissionObjectServerSetupLead = {
	private ["_object","_action","_parameters","_destination_msg","_die_msg","_shared_destination","_shared_alive","_destination_code","_die_code","_destination","_evt_idx","_is_arrived"];
	_object = _this select 0;
	_action = _this select 1;
	_parameters = _this select 2;
	
	if (_object isKindOf "Man") then {
		_destination_msg = _parameters select 0;
		_die_msg = _parameters select 1;
		_shared_destination = _parameters select 2;
		_shared_alive = _parameters select 3;
		_destination = _parameters select 4;
		_destination_code = "";
		if (count _parameters > 5) then {_destination_code = _parameters select 5;};
		_die_code = "";
		if (count _parameters > 6) then {_die_code = _parameters select 6;};
		
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: obj:%3  destmsg:%4  diemsg:%5  dvar:%6  avar:%7  dest:%8  destcode:%9  diecode:%10",diag_tickTime,"mission_functions.sqf","XfMissionObjectServerSetupLead",typeof _object, _destination_msg,_die_msg, _shared_destination, _shared_alive, _destination, (_destination_code != ""), (_die_code != "")];};
		_evt_idx = _object addEventHandler ["killed", { _this call XfMissionEventObjectiveLeadFailed}];
		doStop _object;
		_object setVariable ["mission_lead_failed_evt",_evt_idx];
		_object setCaptive true;
		_object disableAI "AUTOTARGET";
		_object disableAI "MOVE";
		_is_arrived = _object getVariable "mission_person_arrived";
		If (isNil "_is_arrived") then { _object setVariable ["mission_person_arrived",False,True]; };
	
		_dest_center = _destination select 0;
		_dest_size = _destination select 1;
		_dest_angle = _destination select 2;
		_dest_rect = _destination select 3;

		// This will trigger the arrival event.
		[_object,_dest_center,(_dest_size select 0) max (_dest_size select 1)] spawn {
			private ["_person","_destination","_distance"];
			_person = _this select 0;
			_destination = _this select 1;
			_distance = _this select 2;
			// We check that the current distance between the person and the destination is small enough, plus we check the person is not in a vehicle. Or the person is dead.
			waituntil {sleep 20; (((_distance >= (_person distance _destination)) AND (vehicle _person == _person)) OR (! alive _person)); };
			if (alive _person) then {
				_person call XfMissionEventObjectiveLeadSuccess;
			};
		};
	};
};
// When the player begins to lead the person
XfMissionObjectServerExecuteLead = {
	// private ["_object","_action","_parameters","_destination_msg","_die_msg","_shared_destination","_shared_alive","_destination_code","_die_code","_destination"];
	// _object = _this select 0;
	// _action = _this select 1;
	// _parameters = _this select 2;
	// _player = _this select 3;
	// _action_id = _this select 4;
	
	// _destination_msg = _parameters select 0;
	// _die_msg = _parameters select 1;
	// _shared_destination = _parameters select 2;
	// _shared_alive = _parameters select 3;
	// _destination = _parameters select 4;
	// _destination_code = "";
	// if (count _parameters > 5) then {_destination_code = _parameters select 5;};
	// _die_code = "";
	// if (count _parameters > 6) then {_die_code = _parameters select 6;};
	
	// if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: obj:%3  destmsg:%4  diemsg:%5  dvar:%6  avar:%7  dest:%8  destcode:%9  diecode:%10",diag_tickTime,"mission_functions.sqf","XfMissionObjectServerExecuteLead",typeof _object, _destination_msg,_die_msg, _shared_destination, _shared_alive, _destination, (_destination_code != ""), (_die_code != "")];};
};


// ************* ARREST *************
// The person has to be arrested and kept alive
XfMissionObjectServerSetupArrest = {
	private ["_object","_action","_parameters","_arrested_msg","_die_msg","_shared_arrested","_shared_alive","_arrested_code","_die_code","_destination","_evt_idx"];
	_object = _this select 0;
	_action = _this select 1;
	_parameters = _this select 2;
	
	if (_object isKindOf "Man") then {
		_arrested_msg = _parameters select 0;
		_die_msg = _parameters select 1;
		_shared_arrested = _parameters select 2;
		_shared_alive = _parameters select 3;
		_arrested_code = "";
		if (count _parameters > 4) then {_arrested_code = _parameters select 4;};
		_die_code = "";
		if (count _parameters > 5) then {_die_code = _parameters select 5;};
		
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: obj:%3  destmsg:%4  diemsg:%5  dvar:%6  avar:%7  dest:%8  destcode:%9  diecode:%10",diag_tickTime,"mission_functions.sqf","XfMissionObjectServerSetupArrest",typeof _object, _arrested_msg,_die_msg, _shared_arrested, _shared_alive, (_arrested_code != ""), (_die_code != "")];};
		_evt_idx = _object addEventHandler ["killed", { _this call XfMissionEventObjectiveArrestFailed}];
		_object setVariable ["mission_arrest_failed_evt",_evt_idx];

		// This will trigger the arrest event.
		_object spawn {
			private ["_person","_far_entities","_close_entities","_wait","_players_side"];
			_person = _this;
			// We check that there are either no more friendlies in the vicinity or the person is dead or wounded.
			_wait = true;
			_players_side = switch (d_own_side) do {case "EAST": {east};case "WEST": {west};case "GUER": {resistance};case "CIV": {west}; default{west};};
			while {_wait && (alive _person)} do {
				sleep 10;
				_far_entities = (getPos _person) nearEntities [["Man"],100];
				_close_entities = (getPos _person) nearEntities [["Man"],10];
				//if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: Entities: %4  (%5)",diag_tickTime,"mission_functions.sqf","XfMissionObjectServerSetupArrest",_entities,(((side _person) countSide _entities) > 0)];};
				// The person which needs to be arrested has to be damaged a little and also there should be no alive allies in a radius of 200m
				_wait = (
					(
						// As long as there are friendlies less than 100m away even with enemies in the 10m
						(
							(_person countFriendly _far_entities) > 1
						)
						OR
						(
							(_players_side countSide _close_entities) <= 0
						)
					)
					AND
						// As long as it is alive
					(alive _person)
					AND
					(
						// As long as it is not damaged
						(damage _person) < 0.1
					)
				);
			};
			if (alive _person) then {
				_person call XfMissionEventObjectiveArrestSuccess;
			};
		};
	};
};


// ************* TAKEOVER *************
// The zone around the object has to be taken over by one side from another side
XfMissionObjectServerSetupTakeOver = {
	private ["_object","_action","_parameters","_zone_size","_takenover_msg","_lost_msg","_shared_takeover","_hold_time","_owner_side","_taker_side","_takenover_code","_lost_code","_evt_idx"];
	_object = _this select 0;
	_action = _this select 1;
	_parameters = _this select 2;
	
	_zone_size = _parameters select 0;
	_takenover_msg = _parameters select 1;
	_lost_msg = _parameters select 2;
	_shared_takeover = _parameters select 3;
	_hold_time = 20;
	if (count _parameters > 4) then {_hold_time = _parameters select 4;};
	_owner_side = d_side_enemy;
	if (count _parameters > 5) then {_owner_side = _parameters select 5;};
	_taker_side = d_side_player;
	if (count _parameters > 6) then {_taker_side = _parameters select 6;};
	_takenover_code = "";
	if (count _parameters > 7) then {_takenover_code = _parameters select 7;};
	_lost_code = "";
	if (count _parameters > 8) then {_lost_code = _parameters select 8;};
	
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: obj:%4  size:%5  takenmsg:%6  lostmsg:%7  takenvar:%8  ownside:%9  takeside:%10  takecode:%11  lostcode:%12",diag_tickTime,"mission_functions.sqf","XfMissionObjectServerSetupTakeOver",typeof _object,_zone_size, _takenover_msg,_lost_msg, _shared_takeover, _owner_side, _taker_side, (_takenover_code != ""), (_lost_code != "")];};
	_evt_idx = _object addEventHandler ["killed", { _this call XfMissionEventObjectiveTakeOverKilled}];
	_object setVariable ["mission_takeover_killed",_evt_idx,true];
	_object setVariable [_shared_takeover,false];
	// This will trigger the arrest event.
	_object spawn {
		private ["_target","_entities","_taken","_hold_count","_count_owners","_count_takers","_count_soldiers","_action","_parameters","_zone_size","_takenover_msg","_lost_msg","_shared_takeover","_hold_time","_owner_side","_taker_side","_takenover_code","_lost_code","_evt_idx","_share_dist","_grp","_leader","_grps","_wp","_taken_time"];
		_target = _this;
		_action = _target call XfMissionGetAction;
		_parameters = _target call XfMissionGetParameters;
	
		_zone_size = _parameters select 0;
		_takenover_msg = _parameters select 1;
		_lost_msg = _parameters select 2;
		_shared_takeover = _parameters select 3;
		_hold_time = 20;
		if (count _parameters > 4) then {_hold_time = _parameters select 4;};
		_owner_side = d_side_enemy;
		if (count _parameters > 5) then {_owner_side = _parameters select 5;};
		_taker_side = d_side_player;
		if (count _parameters > 6) then {_taker_side = _parameters select 6;};
		_takenover_code = "";
		if (count _parameters > 7) then {_takenover_code = _parameters select 7;};
		_lost_code = "";
		if (count _parameters > 8) then {_lost_code = _parameters select 8;};
		_taken = false;
		_taken_time = 0;
		_share_dist = KRON_UPS_sharedist;
		if (_share_dist > 300) then {_share_dist = 300;};
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: obj:%4  size:%5  takenmsg:%6  lostmsg:%7  takenvar:%8  ownside:%9  takeside:%10  takecode:%11  lostcode:%12",diag_tickTime,"mission_functions.sqf","XfMissionTakeOverChecker",typeof _target,_zone_size, _takenover_msg,_lost_msg, _shared_takeover, _owner_side, _taker_side, (_takenover_code != ""), (_lost_code != "")];};
		while {alive _target && !isnull _target} do {
			_entities = (getPos _target) nearEntities [["Man","Car","Motorcycle","Tank"], _zone_size];
			_count_owners = _owner_side countSide _entities;
			_count_takers = 0;
			if (typeName _taker_side == "ARRAY") then {
				{
					_count_takers = _count_takers + (_x countSide _entities);
				} foreach _taker_side;
			} else {
				_count_takers = _count_takers + (_taker_side countSide _entities);
			};
			// If taker inside we take the bunker
			if ((_count_owners <= 0) and (_count_takers > 0)) then {
				_hold_count = _hold_count + 1;
			};
			// If owner inside, we reown the bunker, two times faster (its the owner after all)
			if ((_count_owners > 0) and (_count_takers <= 0)) then {
				_hold_count = _hold_count - 2;
			};
			// If nobody inside, we set the variable back to the current status
			if ((_count_owners == 0) and (_count_takers == 0)) then {
				if (_taken) then { _hold_count = _hold_time }
				else { _hold_count = 0 };
			};
			if (_hold_count <= 0) then {_hold_count = 0};
			if (_hold_count >= _hold_time) then {_hold_count = _hold_time};
			if ((_hold_count >= _hold_time) && !_taken) then {
				_taken = true;
				[_shared_takeover, _taken] call XNetSetJIP;
				if (_takenover_msg != "") then { _takenover_msg call XfMissionMessageToAll; };
				if (_takenover_code != "") then { ["d_all_exec",[_takenover_code,_target]] call XNetCallEvent; };
				
				// When a bunker gets taken over, we send the nearest group to it
				if (([[(getPos _target),(_zone_size / 2)], _owner_side, _share_dist,["Man","Car","Motorcycle"]] call XfMissionConvergeUnits) > 0) then {
					_taken_time = time;
				};
			};
			if ((_hold_count <= 0) && _taken) then {
				_taken = false;
				[_shared_takeover, _taken] call XNetSetJIP;
				if (_lost_msg != "") then {	_lost_msg call XfMissionMessageToAll; };
				if (_lost_code != "") then { ["d_all_exec",[_lost_code,_target]] call XNetCallEvent; };
			};
			_target setVariable [_shared_takeover,_taken];
			// If still taken after 5 minutes, we call more groups.
			if (_taken && ((time - _taken_time) > 300)) then {
				// Continue to call units if still not retaken but only men who can actually enter
				if (([[(getPos _target),(_zone_size / 2)], _owner_side, _share_dist,["Man"]] call XfMissionConvergeUnits) > 0) then {
					// If a group was called, the function returns a value above 0. This will reset the check time.
					_taken_time = time;
				};
			};
			// if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: obj:%4  owner:%5  taker:%6  taken:%7",diag_tickTime,"mission_functions.sqf","XfMissionObjectServerSetupArrest",typeof _target,_count_owners, _count_takers,_taken];};
			// This helps keeping the CPU activity low
			if (_count_owners == 0 && _count_takers == 0) then { sleep 5 };
			if (_count_owners > 0 && _count_takers == 0 && !_taken) then { sleep 5 };
			if (_count_takers > 0 && _count_owners == 0 && _taken) then { sleep 5 };
			sleep 1;
		};
	};
};

// ===========================================================================
//   						CLIENT SIDE FUNCTIONS
// ===========================================================================

// ************* DESTROY *************
// Object needs to be destoyed by the player.
// Nothing to do on the client side.
XfMissionObjectClientSetupDestroy = {
	// private ["_object","_action","_parameters","_destroyed_msg","_shared_variable","_shared_variable_value","_ammo_amount","_ammo_types","_destroyed_code"];
	// _object = _this select 0;
	// _action = _this select 1;
	// _parameters = _this select 2;
	
	// _destroyed_msg = _parameters select 0;
	// _shared_variable = _parameters select 1;
	// _shared_variable_value = True;
	// if (count _parameters > 2) then {_shared_variable_value = _parameters select 2;};
	// _ammo_amount = 2;
	// if (count _parameters > 3) then {_ammo_amount = _parameters select 3;};
	// _ammo_types = ["PipeBomb"];
	// if (count _parameters > 4) then {if (count (_parameters select 4) > 0) then {_ammo_types = _parameters select 4;};};
	// _destroyed_code = "";
	// if (count _parameters > 5) then {_destroyed_code = _parameters select 5;};
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: Nothing to execute",diag_tickTime,"mission_functions.sqf","XfMissionObjectClientSetupDestroy"];};
};

// ************* PICKUP *************
// The player has to pick the item up
// On client side we need to add the action, so all players have access to it
XfMissionObjectClientSetupPickup = {
	private ["_object","_action","_parameters","_item_desc","_action_title","_pickup_msg","_shared_variable","_shared_variable_value","_pickup_code","_action_id"];
	_object = _this select 0;
	_action = _this select 1;
	_parameters = _this select 2;
	
	_item_desc = _parameters select 0;
	_pickup_msg = _parameters select 1;
	_shared_variable = _parameters select 2;
	_action_title = "Pick Up";
	if (count _parameters > 3) then {_action_title = _parameters select 3;};
	_shared_variable_value = True;
	if (count _parameters > 4) then {_shared_variable_value = _parameters select 4;};
	_pickup_code = "";
	if (count _parameters > 5) then {_pickup_code = _parameters select 5;};
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: obj:%4  desc:%5  msg:%6  var:%7  val:%8  title:%9  code:%10",diag_tickTime,"mission_functions.sqf","XfMissionObjectClientSetupPickup",typeof _object,_item_desc, _pickup_msg, _shared_variable, _shared_variable_value, _action_title, (_pickup_code != "")];};
	
	_object allowDamage false;
	_action_id = _object getVariable "mission_object_pickup_action";
	if !(isnil "_action_id") then { _object removeAction _action_id };
	_action_id = _object addAction [_action_title, "missions\process_action.sqf", [],10,True,True,"","(alive _target)"];
	_object setVariable ["mission_object_pickup_action", _action_id];	
};
XfMissionObjectClientExecutePickup = {
	private ["_object","_action","_parameters","_item_desc","_action_title","_pickup_msg","_shared_variable","_shared_variable_value","_pickup_code","_drop_action","_action_params","_action_priority","_action_condition","_player_objects","_array_index","_forindex","_object_index","_pickedup_objects"];
	_object = _this select 0;
	_action = _this select 1;
	_parameters = _this select 2;
	_player = _this select 3;
	_action_id = _this select 4;
	
	if (_player == player) then {
		_item_desc = _parameters select 0;
		_pickup_msg = _parameters select 1;
		_shared_variable = _parameters select 2;
		_action_title = "Pick Up";
		if (count _parameters > 3) then {_action_title = _parameters select 3;};
		_shared_variable_value = True;
		if (count _parameters > 4) then {_shared_variable_value = _parameters select 4;};
		_pickup_code = "";
		if (count _parameters > 5) then {_pickup_code = _parameters select 5;};
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: obj:%4  desc:%5  msg:%6  var:%7  val:%8  title:%9  code:%10",diag_tickTime,"mission_functions.sqf","XfMissionObjectClientExecutePickup",typeof _object,_item_desc, _pickup_msg, _shared_variable, _shared_variable_value, _action_title, (_pickup_code != "")];};
		d_mission_picked_up_items set [count d_mission_picked_up_items, [_item_desc,(typeof _object),_action,_parameters]];
		// We add the action to the player... since he's not a mission object, he won't be initialized with usable values. Hence why we'll add override variables
		_action_title = Format["Drop %1",_item_desc];
		_action_params = ["DROP",_item_desc];
		_action_priority = -10;
		_action_condition = "alive player";
		_drop_action = player addAction [_action_title, "missions\process_action.sqf", _action_params,_action_priority,False,True,"",_action_condition];
		d_mission_actions_list set [count d_mission_actions_list, [_drop_action,_action_title,_action_params,_action_priority,_action_condition]];
				
		// Now comes the funny part... we store a list of all items picked up by a player. And when he disconnects (willingly or not) we can spawn the items where he last was.
		_player_objects = [];
		_array_index = -1;
		_forindex = 0;
		_pickedup_objects = ["mission_pickedup_objects",[]] call XNetGetJIP;
		if (count _pickedup_objects > 0) then {
			{
				diag_log Format["(%1)[%2]: %3: Player Objects :%4",diag_tickTime,"mission_functions.sqf","XfMissionObjectClientExecutePickup",_x];
				if ((_x select 0) == str(_player)) exitWith {
					_player_objects = _x select 1;
					_array_index = _forindex;
					diag_log Format["(%1)[%2]: %3: Match at :%4",diag_tickTime,"mission_functions.sqf","XfMissionObjectClientExecutePickup",_array_index];
				};
				_forindex = _forindex + 1;
			} foreach _pickedup_objects;
		};
		if (_array_index >= 0) then {
			_object_index = -1;
			if (isNil "_player_objects") then {_player_objects = []};
			If (count _player_objects > 0) then {
				_forindex = 0;
				{
					diag_log Format["(%1)[%2]: %3: Object :%4",diag_tickTime,"mission_functions.sqf","XfMissionObjectClientExecutePickup",_x];
					if (((_x select 0) == _item_desc) and ((_x select 1) == (typeof _object))) exitWith {
						_object_index = _forindex;
					};
					_forindex = _forindex + 1;
				} foreach _player_objects;
			};
			if (_object_index >= 0) then {
				// The item already exists, we don't do anything
			} else {
				_player_objects set[count _player_objects,[_item_desc,(typeof _object),_action,_parameters]];
				diag_log Format["(%1)[%2]: %3: New player objects :%4",diag_tickTime,"mission_functions.sqf","XfMissionObjectClientExecutePickup",_player_objects];
			};
			// We update the array with the new player object list.
			_pickedup_objects set [_array_index,[str(_player),_player_objects]];
		} else {
			_pickedup_objects set [count _pickedup_objects, [str(_player),[[_item_desc,(typeof _object),_action,_parameters]]]];
		};
		["mission_pickedup_objects",_pickedup_objects] call XNetSetJIP;
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: Picked up objects:%4",diag_tickTime,"mission_functions.sqf","XfMissionObjectClientExecutePickup",(["mission_pickedup_objects",[]] call XNetGetJIP)];};
	};
};
// After the object has been picked up, it can be put down aswell (to allow transfer to another player)
XfMissionObjectClientExecuteDrop = {
	private ["_object","_action","_parameters","_player","_action_id","_arguments","_item_desc","_object_array","_pickedup_item","_obj_type","_creation_pos","_new_object","_player_objects","_array_index","_forindex","_object_index","_pickedup_objects"];
	_object = _this select 0; // Contains the player in this particular case, since he possesses the object
	_action = _this select 1;
	_parameters = _this select 2;
	_player = _this select 3;
	_action_id = _this select 4;
	_arguments = _this select 5; // Contain the action name and the reference of the object in the array
	
	//We make sure we only execute on the computer where the player launched the action
	if (_object == player) then {
		_item_desc = _arguments select 1;
		_object_array = objNull;
		// Cannot drop what you don't have...
		if (count d_mission_picked_up_items > 0) then {
			for [{_i=0;},{_i < count d_mission_picked_up_items;},{_i = _i + 1;}] do {
				_pickedup_item = d_mission_picked_up_items select _i;
				if ((_pickedup_item select 0) == _item_desc) exitWith {
					_object_array = _pickedup_item;
				};
			};
		
		};	
		if (!isNil "_object_array") then {
			_obj_type = _object_array select 1;
			_action = _object_array select 2;
			_parameters = _object_array select 3;
			_creation_pos = (getPos Player) findEmptyPosition [0.5,5,_obj_type];
			if (count _creation_pos > 0) then {
				_new_object = createVehicle [_obj_type,_creation_pos,[],1,"NONE"];
				if (!isNull _new_object) then {
					[_new_object,_action] call XfMissionSetAction;
					[_new_object,_parameters] call XfMissionSetParameters;
					if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 - %5(%6)",diag_tickTime,"mission_functions.sqf","Registering the object",(str _new_object),_action,count _parameters];};
					_new_object call XfMissionAddObject;
					["d_all_mobject",_new_object] call XNetCallEvent;
					["d_msg",[Format["%1 has dropped %2",name player,_item_desc],"all",objNull,"hq"]] call XNetCallEvent;
					player removeAction _action_id;
					player reveal _new_object;
					_player_objects = [];
					_array_index = -1;
					_forindex = 0;
					_pickedup_objects = ["mission_pickedup_objects",[]] call XNetGetJIP;
					if (count _pickedup_objects > 0) then {
						{
							if ((_x select 0) == str(_player)) exitWith {
								_player_objects = _x select 1;
								_array_index = _forindex;
							};
							_forindex = _forindex + 1;
						} foreach _pickedup_objects;
					};
					diag_log Format["(%1)[%2]: %3: %4: %5",diag_tickTime,"mission_functions.sqf","XfMissionObjectClientExecuteDrop","Player Objects",_player_objects];
					if (_array_index >= 0) then {
						if (isNil "_player_objects") then {_player_objects = []};
						If (count _player_objects > 0) then {
							_object_index = -1;
							_forindex = 0;
							{
								diag_log Format["(%1)[%2]: %3: %4: %5",diag_tickTime,"mission_functions.sqf","XfMissionObjectClientExecuteDrop","    Object ",_x];
								if (((_x select 0) == _item_desc) and ((_x select 1) == _obj_type)) exitWith {
									_object_index = _forindex;
								};
								_forindex = _forindex + 1;
							} foreach _player_objects;
						};
						if (_object_index >= 0) then {
							// The item is found, we remove it.
							_player_objects set [_object_index, "xXxDELxXx"];
							_player_objects = _player_objects - ["xXxDELxXx"];
						} else {
							diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionObjectClientExecuteDrop","The player doesn't have this object"];
						};
						// We update the array with the new player object list.
						_pickedup_objects set [_array_index,[str(_player),_player_objects]];
						["mission_pickedup_objects",_pickedup_objects] call XNetSetJIP;
					} else {
						// The player has no object nothing to do
						diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionObjectClientExecuteDrop","The player has no objects listed"];
					};
					if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: Picked up objects:%4",diag_tickTime,"mission_functions.sqf","XfMissionObjectClientExecuteDrop",(["mission_pickedup_objects",[]] call XNetGetJIP)];};
				};
			} else {
				"No suitable position to drop the item!" call XfGlobalChat;
			};
		} else {
			"The item was not found!" call XfGlobalChat;
			player removeAction _action_id;
		};
	};
};

// ************* ACTIVATE *************
// Object can be activated/deactivated by the player
// We make the object indestructible
XfMissionObjectClientSetupActivate = {
	private ["_object","_action","_parameters","_action_title","_activated_msg","_deactivated_msg","_shared_variable","_initial_status","_activated_title","_deactivated_title","_activated_code","_deactivated_code","_action_id"];
	_object = _this select 0;
	_action = _this select 1;
	_parameters = _this select 2;
	
	_activated_msg = _parameters select 0;
	_deactivated_msg = _parameters select 1;
	_shared_variable = _parameters select 2;
	_activated_title = "Deactivate";
	if (count _parameters > 3) then {_activated_title = _parameters select 3;};
	_deactivated_title = "Activate";
	if (count _parameters > 4) then {_deactivated_title = _parameters select 4;};
	_initial_status = False;
	if (count _parameters > 5) then {_initial_status = _parameters select 5;};
	_activated_code = "";
	if (count _parameters > 6) then {_activated_code = _parameters select 6;};
	_deactivated_code = "";
	if (count _parameters > 7) then {_deactivated_code = _parameters select 7;};
	
	_action_title = _activated_title;
	If (!_initial_status) then { _action_title = _deactivated_title; };
	_object setVariable ["mission_object_activation_previous_status",_initial_status];
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: obj:%4  actm:%5 deactm:%6  var:%7  val:%8  actt:%9  deactt:%10  sts:%11  actc:%12  deactc:%13",diag_tickTime,"mission_functions.sqf","XfMissionObjectClientSetupActivate",typeof _object, _activated_msg, _deactivated_msg, _shared_variable, _initial_status, _activated_title, _deactivated_title, _initial_status, (_activated_code != ""),(_deactivated_code != "")];};
	if (_action_title != "") then {	
		_action_id = _object getVariable "mission_object_activation_action";
		if !(isnil "_action_id") then { _object removeAction _action_id };
		_action_id = _object addAction [_action_title, "missions\process_action.sqf", [],10,True,True,"","(alive _target)"];
		// We set the variable as local because it is dependent on the computer running it.
		_object setVariable ["mission_object_activation_action",_action_id];
	};
};
XfMissionObjectClientExecuteActivate = {
	private ["_object","_action","_parameters","_current_status","_action_title","_activated_msg","_deactivated_msg","_shared_variable","_initial_status","_activated_title","_deactivated_title","_activated_code","_deactivated_code","_previous_status","_stored_action_id"];
	_object = _this select 0;
	_action = _this select 1;
	_player = _this select 3;
	_action_id = _this select 4;
	_parameters = _this select 2;
	_activated_msg = _parameters select 0;
	_deactivated_msg = _parameters select 1;
	_shared_variable = _parameters select 2;
	_activated_title = "Deactivate";
	if (count _parameters > 3) then {_activated_title = _parameters select 3;};
	_deactivated_title = "Activate";
	if (count _parameters > 4) then {_deactivated_title = _parameters select 4;};
	_initial_status = False;
	if (count _parameters > 5) then {_initial_status = _parameters select 5;};
	_activated_code = "";
	if (count _parameters > 6) then {_activated_code = _parameters select 6;};
	_deactivated_code = "";
	if (count _parameters > 7) then {_deactivated_code = _parameters select 7;};

	_stored_action_id = _object getVariable "mission_object_activation_action_id";
	if (isNil "_stored_action_id") then { _stored_action_id = -1 };
	if (_stored_action_id >= 0) then {_action_id = _stored_action_id};
	_object removeAction _action_id;
	_current_status = _object getVariable "mission_object_activation_status";
	if (isnil "_current_status") then {_current_status = _initial_status};
	_previous_status = _object getVariable "mission_object_activation_previous_status";
	if (isnil "_previous_status") then {_previous_status = _current_status};
	// Both previous and current status should be different. If it isn't the case, then we switch Current.
	if (!((_current_status || _previous_status) && !(_current_status && _previous_status))) then {
		_current_status = !_current_status;
	};	
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: obj:%4  actm:%5 deactm:%6  var:%7  val:%8  actt:%9  deactt:%10  sts:%11  actc:%12  deactc:%13",diag_tickTime,"mission_functions.sqf","XfMissionObjectClientExecuteActivate",typeof _object, _activated_msg, _deactivated_msg, _shared_variable, _shared_variable_value, _activated_title, _deactivated_title, _current_status, (_activated_code != ""),(_deactivated_code != "")];};
	If (isNil "_current_status") then {
		_current_status = _initial_status;
		_current_status = !_current_status;
	};
	_object setVariable ["mission_object_activation_previous_status",_current_status];

	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: obj:%4  actm:%5 deactm:%6  var:%7  val:%8  actt:%9  deactt:%10  sts:%11  actc:%12  deactc:%13",diag_tickTime,"mission_functions.sqf","XfMissionObjectClientExecuteActivate",typeof _object, _activated_msg, _deactivated_msg, _shared_variable, _shared_variable_value, _activated_title, _deactivated_title, _current_status, (_activated_code != ""),(_deactivated_code != "")];};
	_action_title = _activated_title;
	If (! _current_status) then { _action_title = _deactivated_title; };
	// If the activation title has been left empty, it means we don't want to allow changing the state back.
	if (_action_title != "") then {
		_action_id = _object getVariable "mission_object_activation_action";
		if !(isnil "_action_id") then { _object removeAction _action_id };
		_action_id = _object addAction [_action_title, "missions\process_action.sqf", [],10,True,True,"","true"];
		// We set the variable as local because it is dependent on the computer running it.
		_object setVariable ["mission_object_activation_action",_action_id];
	};
};

// ************* ASSASSINATE *************
// Object can be activated/deactivated by the player
// We make the object indestructible
XfMissionObjectClientSetupAssassinate = {
	private ["_object","_action","_parameters","_killed_msg","_shared_variable","_killed_code"];
	_object = _this select 0;
	_action = _this select 1;
	_parameters = _this select 2;
	
	if ((_object isKindOf "Man") Or (_object isKindOf "LandVehicle") Or (_object isKindOf "Air") Or (_object isKindOf "Ship")) then {
		if (count _parameters > 2) then {_killed_code = _parameters select 2;};
		_evt_idx = _object getVariable "mission_killed_evt";
		if  (isnil "_evt_idx") then {
			_evt_idx = _object addEventHandler ["killed", { _this call XfMissionEventObjectiveKilled}];
			_object setVariable ["mission_killed_evt",_evt_idx];
		};
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: Killed handler added",diag_tickTime,"mission_functions.sqf","XfMissionObjectServerSetupAssassinate"];};
	} else {
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 %5",diag_tickTime,"mission_functions.sqf","XfMissionObjectServerSetupAssassinate","Is not a valid class", _object];};
	};
};

// ************* CONQUER *************
// The area needs to be cleared of all enemies.
XfMissionObjectClientSetupConquer = {
	// private ["_object","_action","_parameters","_conquer_msg","_conquer_zone","_conquer_side","_shared_variable","_shared_variable_value","_conquer_code","_conquer_zone_center","_conquer_zone_size","_conquer_zone_angle","_conquer_zone_rect","_conquer_left"];
	// _object = _this select 0;
	// _action = _this select 1;
	// _parameters = _this select 2;

	// _conquer_msg = _parameters select 0;
	// _conquer_zone = _parameters select 1;
	// _conquer_side = _parameters select 2;
	// _shared_variable = _parameters select 3;
	// _conquer_left = 3;
	// if (count _parameters > 4) then {_conquer_left = _parameters select 4};
	// _conquer_code = "";
	// if (count _parameters > 5) then {_conquer_code = _parameters select 5;};
	// _conquer_zone_center = _conquer_zone select 0;
	// _conquer_zone_size = _conquer_zone select 1;
	// _conquer_zone_angle = _conquer_zone select 2;
	// _conquer_zone_rect = _conquer_zone select 3;
	// if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: msg:%4  zone:%5  objid:%6  var:%7  code:%8",diag_tickTime,"mission_functions.sqf","XfMissionObjectClientSetupConquer",_conquer_msg,_conquer_zone,_object_identifier,_shared_variable,(_conquer_code != "")];};
};

// ************* RESCUE *************
// The person has to be freed by players
// We add the "Free" action on the person
XfMissionObjectClientSetupRescue = {
	private ["_object","_action","_parameters","_freed_msg","_die_msg","_shared_free","_shared_alive","_free_code","_die_code","_evt_idx","_action_id"];
	_object = _this select 0;
	_action = _this select 1;
	_parameters = _this select 2;
	
	if (_object isKindOf "Man") then {
		_freed_msg = _parameters select 0;
		_die_msg = _parameters select 1;
		_shared_free = _parameters select 2;
		_shared_alive = _parameters select 3;
		_free_code = "";
		if (count _parameters > 4) then {_free_code = _parameters select 4;};
		_die_code = "";
		if (count _parameters > 5) then {_die_code = _parameters select 5;};
		
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: obj:%4  fmsg:%5  dmsg:%6  fvar:%7  avar:%8  fcode:%9  dcode:%10",diag_tickTime,"mission_functions.sqf","XfMissionObjectClientSetupRescue",typeof _object, _freed_msg, _die_msg, _shared_free, _shared_alive, (_free_code != ""), (_die_code != "")];};
		_action_id = _object getVariable "mission_rescue_action_id";
		if (!isnil "_action_id") then {
			_object removeAction _action_id;
		};
		_evt_idx = _object getVariable "mission_rescue_failed_evt";
		if  (isnil "_evt_idx") then {
			_evt_idx = _object addEventHandler ["killed", { _this call XfMissionEventObjectiveRescueFailed}];
			_object setVariable ["mission_rescue_failed_evt",_evt_idx];
		};
		_object setCaptive true;

		_action_id = _object addAction ["Free", "missions\process_action.sqf", [],10,True,True,"","(alive _target) && (captive _target)"];
		_object setVariable ["mission_rescue_action_id",_action_id];
		// The following code will play the captive animation until the hostage is freed or dead.
		_object spawn {
			private ["_hostage","_curr_anim"];
			_hostage = _this;
			if (d_mission_debug_messages) then { diag_log format["hostage start: %1 (%2)",_hostage, (_hostage isKindOf "Woman")];};
			While {captive _hostage && alive _hostage} do {
				_move = format["CivilSitting0%1",(floor(random(3))+1)];
				// Women need another animation.
				if (_hostage isKindOf "Woman") then {_move = format["amovpercmstpslowwrfldnon_amovpsitmstpsraswrfldnon",(floor(random(3))+1)];};
				_hostage switchMove _move;
				// _animstate = animationState _hostage;
				waituntil {"amovpercmstpslowwrfldnon_amovpsitmstpsraswrfldnon" != animationState _hostage};
				sleep 1.234;
			};
			if (d_mission_debug_messages) then { diag_log format["hostage end: %1 (%2)",_hostage, alive _hostage];};
			if (alive _hostage) then {
				_hostage switchMove "SitStandUp";
			};
		};
	};
};
// When the hostage is freed, we remove the action
XfMissionObjectClientExecuteRescue = {
	private ["_object","_action","_parameters","_player","_action_id","_freed_msg","_die_msg","_shared_free","_shared_alive","_free_code","_die_code","_evt_idx","_obj_act_id"];
	_object = _this select 0;
	_action = _this select 1;
	_parameters = _this select 2;
	_player = _this select 3;
	_action_id = _this select 4;
	
	_freed_msg = _parameters select 0;
	_die_msg = _parameters select 1;
	_shared_free = _parameters select 2;
	_shared_alive = _parameters select 3;
	_free_code = "";
	if (count _parameters > 4) then {_free_code = _parameters select 4;};
	_die_code = "";
	if (count _parameters > 5) then {_die_code = _parameters select 5;};	
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: obj:%4  fmsg:%5  dmsg:%6  fvar:%7  avar:%8  fcode:%9  dcode:%10  actid:%11",diag_tickTime,"mission_functions.sqf","XfMissionObjectClientExecuteRescue",typeof _object, _freed_msg, _die_msg, _shared_free, _shared_alive, (_free_code != ""), (_die_code != ""),_action_id];};

	_object setCaptive false;

	_obj_act_id = _object getVariable "mission_rescue_action_id";
	if (!isnil "_obj_act_id") then {
		if (_obj_act_id != _action_id) then { _object removeAction _obj_act_id; };
	};
	_object removeAction _action_id;

	_evt_idx = _object getVariable "mission_rescue_failed_evt";
	if (! isnil "_evt_idx") then {_object removeEventHandler ["killed", _evt_idx]; _object setVariable ["mission_rescue_failed_evt",nil];};
};


// ************* LEAD *************
// The person has to be led to a destination and kept alive
XfMissionObjectClientSetupLead = {
	private ["_object","_action","_parameters","_destination_msg","_die_msg","_shared_destination","_shared_alive","_destination_code","_die_code","_destination","_evt_idx","_is_arrived"];
	_object = _this select 0;
	_action = _this select 1;
	_parameters = _this select 2;

	_destination_msg = _parameters select 0;
	_die_msg = _parameters select 1;
	_shared_destination = _parameters select 2;
	_shared_alive = _parameters select 3;
	_destination = _parameters select 4;
	_destination_code = "";
	if (count _parameters > 5) then {_destination_code = _parameters select 5;};
	_die_code = "";
	if (count _parameters > 6) then {_die_code = _parameters select 6;};
	
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: obj:%3  destmsg:%4  diemsg:%5  dvar:%6  avar:%7  dest:%8  destcode:%9  diecode:%10",diag_tickTime,"mission_functions.sqf","XfMissionObjectClientSetupLead",typeof _object, _destination_msg,_die_msg, _shared_destination, _shared_alive, _destination, (_destination_code != ""), (_die_code != "")];};

	_is_arrived = _object getVariable "mission_person_arrived";
	If (isNil "_is_arrived") then { _is_arrived = false; _object setVariable ["mission_person_arrived",False,True]; };
	If ((!_is_arrived) and (d_player_can_rescue)) then {
		// By setting the action condition, we'll make it disappear everytime the unit is led by someone.
		_action_id = _object getVariable "mission_lead_action";
		if !(isnil "_action_id") then { _object removeAction _action_id };
		_action_id = _object addAction ["Lead", "missions\process_action.sqf", [],10,True,True,"","(((leader _target == _target) and (alive _target)) AND (!(_target getVariable 'mission_person_arrived')))"];
		_object setVariable ["mission_lead_action",_action_id];
		_evt_idx = _object getVariable "mission_lead_failed_evt";
		if  (isnil "_evt_idx") then {
			_evt_idx = _object addEventHandler ["killed", { _this call XfMissionEventObjectiveLeadFailed}];
			_object setVariable ["mission_lead_failed_evt",_evt_idx];
		};
	};
};
// When the player begins to lead the person
XfMissionObjectClientExecuteLead = {
	private ["_object","_action","_parameters","_destination_msg","_die_msg","_shared_destination","_shared_alive","_destination_code","_die_code","_destination"];
	_object = _this select 0;
	_action = _this select 1;
	_parameters = _this select 2;
	_player = _this select 3;
	_action_id = _this select 4;
	
	if (_player == player) then {
		// See i_client.sqf and setupplayer.sqf for this var
		if (d_player_can_rescue) then {
			_destination_msg = _parameters select 0;
			_die_msg = _parameters select 1;
			_shared_destination = _parameters select 2;
			_shared_alive = _parameters select 3;
			_destination = _parameters select 4;
			_destination_code = "";
			if (count _parameters > 5) then {_destination_code = _parameters select 5;};
			_die_code = "";
			if (count _parameters > 6) then {_die_code = _parameters select 6;};
			
			if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: obj:%3  destmsg:%4  diemsg:%5  dvar:%6  avar:%7  dest:%8  destcode:%9  diecode:%10",diag_tickTime,"mission_functions.sqf","XfMissionObjectClientExecuteLead",typeof _object, _destination_msg,_die_msg, _shared_destination, _shared_alive, _destination, (_destination_code != ""), (_die_code != "")];};

			_object setCaptive false;
			_object enableAI "MOVE";

			[_object] joinSilent group _player;
			sleep 2;
			_object doMove getPos _player;
			sleep 0.1;
			_object doFollow _player;
			(group _player) setBehaviour "SAFE";
			(group _player) setSpeedMode "FULL";
			_object doFollow _player;
			
			[_object,_player] spawn {
				_person = _this select 0;
				_leader = _this select 1;
				// In case the person or the leader dies, we dissociate both
				waituntil {sleep 10; ((leader _person != _leader) OR (!alive _person) OR (!alive _leader))};
				doStop _person;
				_person disableAI "MOVE";
				[_person] joinSilent grpNull;
				doStop _person;
				if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"mission_functions.sqf","XfMissionObjectClientExecuteLead","exited group verification"];};
			};
		} else {
			"You are not part of the Rescue Leaders!" call XfGlobalChat;
		};
	};
};


// ************* ARREST *************
// The person has to be arrested and kept alive
XfMissionObjectClientSetupArrest = {
	private ["_object","_action","_parameters","_arrested_msg","_die_msg","_shared_arrested","_shared_alive","_arrested_code","_die_code","_evt_idx"];
	_object = _this select 0;
	_action = _this select 1;
	_parameters = _this select 2;
	
	if (_object isKindOf "Man") then {
		_arrested_msg = _parameters select 0;
		_die_msg = _parameters select 1;
		_shared_arrested = _parameters select 2;
		_shared_alive = _parameters select 3;
		_arrested_code = "";
		if (count _parameters > 4) then {_arrested_code = _parameters select 4;};
		_die_code = "";
		if (count _parameters > 5) then {_die_code = _parameters select 5;};
		_evt_idx = _object getVariable "mission_arrest_failed_evt";
		if  (isnil "_evt_idx") then {
			_evt_idx = _object addEventHandler ["killed", { _this call XfMissionEventObjectiveArrestFailed}];
			_object setVariable ["mission_arrest_failed_evt",_evt_idx];
		};
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: obj:%4  amsg:%5  diemsg:%6  avar:%7  dvar:%8  acode:%9  diecode:%10",diag_tickTime,"mission_functions.sqf","XfMissionObjectClientSetupArrest",typeof _object, _arrested_msg,_die_msg, _shared_arrested, _shared_alive, (_arrested_code != ""), (_die_code != "")];};
	};
};


// ************* TAKEOVER *************
// The zone around the object has to be taken over by one side from another side
XfMissionObjectClientSetupTakeOver = {
	private ["_object","_action","_parameters","_zone_size","_takenover_msg","_lost_msg","_shared_takeover","_hold_time","_owner_side","_taker_side","_takenover_code","_lost_code","_evt_idx"];
	_object = _this select 0;
	_action = _this select 1;
	_parameters = _this select 2;
	
	// _zone_size = _parameters select 0;
	// _takenover_msg = _parameters select 1;
	// _lost_msg = _parameters select 2;
	// _shared_takeover = _parameters select 3;
	// _hold_time = 20;
	// if (count _parameters > 4) then {_hold_time = _parameters select 4;};
	// _owner_side = d_side_enemy;
	// if (count _parameters > 5) then {_owner_side = _parameters select 5;};
	// _taker_side = d_side_player;
	// if (count _parameters > 6) then {_taker_side = _parameters select 6;};
	// _takenover_code = "";
	// if (count _parameters > 7) then {_takenover_code = _parameters select 7;};
	// _lost_code = "";
	// if (count _parameters > 8) then {_lost_code = _parameters select 8;};
	
	// if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: obj:%4  size:%5  takenmsg:%6  lostmsg:%7  takenvar:%8  ownside:%9  takeside:%10  takecode:%11  lostcode:%12",diag_tickTime,"mission_functions.sqf","XfMissionObjectClientSetupTakeOver",typeof _object,_zone_size, _takenover_msg,_lost_msg, _shared_takeover, _owner_side, _taker_side, (_takenover_code != ""), (_lost_code != "")];};
};


// ===========================================================================
//   						OBJECT FUNCTIONS
// ===========================================================================

// Handles the damage done to an object which was initialized with the "destroy" type.
XfMissionEventObjectiveDestroy = {
	if (!isServer) exitWith{};
	private ["_object","_damage","_causedBy","_ammunition","_ammo_string","_parameters","_destroyed_msg","_shared_variable","_shared_variable_value","_ammo_amount","_ammo_types","_destroyed_code", "_damaged"];
	_object = _this select 0;
	_damage = _this select 2;
	_causedBy = _this select 3;
	_ammunition = _this select 4;
	_action = _object call XfMissionGetAction;

	_parameters = _object call XfMissionGetParameters;
	_destroyed_msg = _parameters select 0;
	_shared_variable = _parameters select 1;
	_shared_variable_value = True;
	if (count _parameters > 2) then {_shared_variable_value = _parameters select 2;};
	_ammo_amount = 2;
	if (count _parameters > 3) then {_ammo_amount = _parameters select 3;};
	_ammo_types = ["PipeBomb"];
	if (count _parameters > 4) then {if (typeName (_parameters select 4) == "ARRAY") then {if (count (_parameters select 4) > 0) then {_ammo_types = _parameters select 4;};};};
	_destroyed_code = "";
	if (count _parameters > 5) then {_destroyed_code = _parameters select 5;};

	_ammo_string = str(_ammunition);
	if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: obj:%4  cause:%5  ammo:%6  dmg:%7  msg:%8  var:%9  val:%10  amnt:%11  types:%12  match:%13  code:%14",diag_tickTime,"mission_functions.sqf","XfMissionEventObjectiveDestroy",typeof _object,_causedBy,_ammunition, _damage, _destroyed_msg, _shared_variable, _shared_variable_value, _ammo_amount, _ammo_types,(_ammo_types find _ammunition), (_destroyed_code != "")];};
	_damaged = false;
	if (((_ammo_types find _ammo_string) >= 0) OR ((_ammo_types find _ammunition) >= 0)) then {
		// We need sufficient damage to take it into account.
		If (_damage > 0.2) then { _damaged = true; };
	} else {
		if (d_mission_debug_messages) then { diag_log Format["Ammo String %1, %2",_ammo_string,(_ammo_string == str(""))];};
		// Apparently with small moveable items, the ammunition parameter doesn't go through. So we just accept it as is. It's only a workaround... not perfect but... eh...
		If ((_ammo_string == str("")) AND (_causedBy == _object)) then {
			_damaged = true;
		};
	};
	
	If (_damaged) then {
		_ammo_amount = _ammo_amount - 1;
		_parameters set [3, _ammo_amount];
		_object setVariable ["mission_action_parameters",(_parameters),True];
		If (_ammo_amount <= 0) then {
			_object removeAllEventHandlers "HandleDamage";
			_object removeAllEventHandlers "Hit";
			_object allowDamage True;
			_object setDamage 1.1;
			If (_destroyed_msg != "") then { ["d_msg",[_destroyed_msg,"all",objNull,"hq"]] call XNetCallEvent; };
			if (!isNil "_shared_variable") then {
				if (_shared_variable != "") then {[_shared_variable,_shared_variable_value] call XNetSetJIP;};
			};
			if (_destroyed_code != "") then {
				["d_all_exec",[_destroyed_code,_object]] call XNetCallEvent;
			};
			// For small non destructible objects, we need to delete them ourselves.
			If ((_ammo_string == str("")) AND (_causedBy == _object)) then {
				deleteVehicle _object;
			};
			if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: Destroyed! obj:%4  var:%5  val:%6",diag_tickTime,"mission_functions.sqf","XfMissionEventObjectiveDestroy",typeof _object,_shared_variable, _shared_variable_value];};
		};		
	};
	0;
};

XfMissionEventObjectiveKilled = {
	if (isServer) then {
		private ["_object","_action","_parameters","_killed_msg","_shared_variable","_killed_code","_evt_idx"];
		_object = _this select 0;
		_killer = _this select 1;
		_parameters = _object call XfMissionGetParameters;
		_action = _object call XfMissionGetAction;
		
		// Avoids we execute two times.
		_status = _object getVariable "mission_action_event_executed";
		if (_action == "ASSASSINATE" && !_status) then {
			_object setVariable ["mission_action_event_executed",true];
			
			_killed_msg = _parameters select 0;
			_shared_variable = _parameters select 1;
			_killed_code = "";
			if (count _parameters > 2) then {_killed_code = _parameters select 2;};
			_evt_idx = _object getVariable "mission_killed_evt";
			If (! isNil "_evt_idx") then {	_object removeEventHandler ["killed",_evt_idx]; };
			if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: kmsg:%4   var:%5   code:%6",diag_tickTime,"mission_functions.sqf","XfMissionEventObjectiveKilled",_killed_msg,_shared_variable,(_killed_code != "")];};
			If (_killed_msg != "") then { ["d_msg",[_killed_msg,"all",objNull,"hq"]] call XNetCallEvent; };
			if (!isNil "_shared_variable") then {
				if (_shared_variable != "") then {[_shared_variable,true] call XNetSetJIP;};
			};
			if (_killed_code != "") then {
				["d_all_exec",[_killed_code,_object]] call XNetCallEvent;
			};
		};
	} else {
		["d_s_mexecute", ["XfMissionEventObjectiveKilled",_this]] call XNetCallEvent;
		_evt_idx = _object getVariable "mission_killed_evt";
		If (! isNil "_evt_idx") then {	_object removeEventHandler ["killed",_evt_idx]; _object setVariable ["mission_killed_evt", nil]; };
	};
};

XfMissionEventObjectiveConquer = {
	// It does only make sense on the server. And normally the trigger is local to it anyway :)
	if (isServer) then {
		private ["_trigger","_object_id","_object","_parameters","_conquer_msg","_conquer_zone","_conquer_side","_shared_variable","_shared_variable_value","_conquer_code","_conquer_left"];
		_trigger = _this select 0;
		_object_id = _this select 1;
		_list = list _trigger;

		_object = objNull;
		{
			If ((_x getVariable "mission_object_identifier") == _object_id) exitWith {_object = _x};
		} foreach d_mission_object_list;
		if (!isNull _object) then {
			_parameters = _object call XfMissionGetParameters;

			_conquer_msg = _parameters select 0;
			_conquer_zone = _parameters select 1;
			_conquer_side = _parameters select 2;
			_shared_variable = _parameters select 3;
			_conquer_left = 3;
			if (count _parameters > 4) then {_conquer_left = _parameters select 4};
			_conquer_code = "";
			if (count _parameters > 5) then {_conquer_code = _parameters select 5;};

			if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: msg:%4  zone:%5  objid:%6  var:%7  code:%8  list:%9",diag_tickTime,"mission_functions.sqf","XfMissionEventObjectiveConquer",_conquer_msg,_conquer_zone,_object_identifier,_shared_variable,(_conquer_code != ""),_list];};
			If (_conquer_msg != "") then { ["d_msg",[_conquer_msg,"all",objNull,"hq"]] call XNetCallEvent; };
			if (!isNil "_shared_variable") then {
				if (_shared_variable != "") then {[_shared_variable,true] call XNetSetJIP;};
			};
			if (_conquer_code != "") then {
				["d_all_exec",[_conquer_code,_object]] call XNetCallEvent;
			};
			_trigger setTriggerStatements ["false", "", ""];
		} else {
			if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: %4 (%5)",diag_tickTime,"mission_functions.sqf","XfMissionEventObjectiveConquer","The object was not found in the list!",_object_id];};
		};
	};
};

XfMissionEventObjectiveRescueFailed = {
	if (isServer) then {
		private ["_object","_action","_parameters","_freed_msg","_die_msg","_shared_free","_shared_alive","_free_code","_die_code","_evt_idx"];
		_object = _this select 0;
		_killer = _this select 1;
		_parameters = _object call XfMissionGetParameters;
		_action = _object call XfMissionGetAction;
		
		// Avoids we execute two times.
		_status = _object getVariable "mission_action_event_executed";
		if (_action == "RESCUE" && !_status) then {
			_object setVariable ["mission_action_event_executed",true];

			if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: input:%4",diag_tickTime,"mission_functions.sqf","XfMissionEventObjectiveRescueFailed",_this];};
			_freed_msg = _parameters select 0;
			_die_msg = _parameters select 1;
			_shared_free = _parameters select 2;
			_shared_alive = _parameters select 3;
			_free_code = "";
			if (count _parameters > 4) then {_free_code = _parameters select 4;};
			_die_code = "";
			if (count _parameters > 5) then {_die_code = _parameters select 5;};
				
			_action_id = _object getVariable "mission_rescue_action_id";
			if (!isNil "_action_id") then { _object removeAction _action_id; };
			_evt_idx = _object getVariable "mission_rescue_failed_evt";
			If (! isNil "_evt_idx") then {	_object removeEventHandler ["killed",_evt_idx]; };
			if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: obj:%4  fmsg:%5  dmsg:%6  fvar:%7  avar:%8  fcode:%9  dcode:%10",diag_tickTime,"mission_functions.sqf","XfMissionEventObjectiveRescueFailed",typeof _object, _freed_msg, _die_msg, _shared_free, _shared_alive, (_free_code != ""), (_die_code != "")];};

			If (_die_msg != "") then { ["d_msg",[_die_msg,"all",objNull,"hq"]] call XNetCallEvent; };
			if (!isNil "_shared_alive") then {
				if (_shared_alive != "") then {[_shared_alive,false] call XNetSetJIP;};
			};
			if (_die_code != "") then {
				["d_all_exec",[_die_code,_object]] call XNetCallEvent;
			};		
		};
	} else {
		["d_s_mexecute", ["XfMissionEventObjectiveRescueFailed",_this]] call XNetCallEvent;
		_evt_idx = _object getVariable "mission_rescue_failed_evt";
		If (! isNil "_evt_idx") then {	_object removeEventHandler ["killed",_evt_idx]; _object setVariable ["mission_rescue_failed_evt", nil]; };
	};
};

XfMissionEventObjectiveLeadFailed = {
	if (isServer) then {
		// TODO: We can add a variable on the object to make sure we execute only once.
		private ["_object","_action","_parameters","_destination_msg","_die_msg","_shared_destination","_shared_alive","_destination_code","_die_code","_destination","_evt_idx"];
		_object = _this select 0;
		_killer = _this select 1;
		_parameters = _object call XfMissionGetParameters;
		_action = _object call XfMissionGetAction;
		
		// Avoids we execute two times.
		_status = _object getVariable "mission_action_event_executed";
		if (_action == "LEAD" && !_status) then {
			_object setVariable ["mission_action_event_executed",true];

			_destination_msg = _parameters select 0;
			_die_msg = _parameters select 1;
			_shared_destination = _parameters select 2;
			_shared_alive = _parameters select 3;
			_destination = _parameters select 4;
			_destination_code = "";
			if (count _parameters > 5) then {_destination_code = _parameters select 5;};
			_die_code = "";
			if (count _parameters > 6) then {_die_code = _parameters select 6;};
			
			_action_id = _object getVariable "mission_lead_action_id";
			if (!isNil "_action_id") then { _object removeAction _action_id; };
			if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: obj:%4  destmsg:%5  diemsg:%6  dvar:%7  avar:%8  dest:%9  destcode:%10  diecode:%11",diag_tickTime,"mission_functions.sqf","XfMissionEventObjectiveLeadFailed",typeof _object, _destination_msg,_die_msg, _shared_destination, _shared_alive, _destination, (_destination_code != ""), (_die_code != "")];};
			_evt_idx = _object getVariable "mission_lead_failed_evt";
			If (! isNil "_evt_idx") then {	_object removeEventHandler ["killed",_evt_idx]; };

			If (_die_msg != "") then { ["d_msg",[_die_msg,"all",objNull,"hq"]] call XNetCallEvent; };
			if (!isNil "_shared_alive") then {
				if (_shared_alive != "") then {[_shared_alive,false] call XNetSetJIP;};
			};
			if (_die_code != "") then {
				["d_all_exec",[_die_code,_object]] call XNetCallEvent;
			};		
		};
	} else {
		["d_s_mexecute", ["XfMissionEventObjectiveLeadFailed",_this]] call XNetCallEvent;
		_evt_idx = _object getVariable "mission_lead_failed_evt";
		If (! isNil "_evt_idx") then {	_object removeEventHandler ["killed",_evt_idx]; _object setVariable ["mission_lead_failed_evt", nil]; };
	};

};

// This is executed when the person reaches the destination
XfMissionEventObjectiveLeadSuccess = {
	if (isServer) then {
		// TODO: We can add a variable on the object to make sure we execute only once.
		private ["_object","_action","_parameters","_destination_msg","_die_msg","_shared_destination","_shared_alive","_destination_code","_die_code","_destination","_evt_idx"];
		_object = _this;
		_parameters = _object call XfMissionGetParameters;
		_action = _object call XfMissionGetAction;
		
		// Avoids we execute two times.
		_status = _object getVariable "mission_action_event_executed";
		if (_action == "LEAD" && !_status) then {
			_object setVariable ["mission_action_event_executed",true];

			_destination_msg = _parameters select 0;
			_die_msg = _parameters select 1;
			_shared_destination = _parameters select 2;
			_shared_alive = _parameters select 3;
			_destination = _parameters select 4;
			_destination_code = "";
			if (count _parameters > 5) then {_destination_code = _parameters select 5;};
			_die_code = "";
			if (count _parameters > 6) then {_die_code = _parameters select 6;};
			
			if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: obj:%4  destmsg:%5  diemsg:%6  dvar:%7  avar:%8  dest:%9  destcode:%10  diecode:%11",diag_tickTime,"mission_functions.sqf","XfMissionEventObjectiveLeadFailed",typeof _object, _destination_msg,_die_msg, _shared_destination, _shared_alive, _destination, (_destination_code != ""), (_die_code != "")];};

			doStop _object;
			[_object] joinsilent grpNull;
			doStop _object;
			_evt_idx = _object getVariable "mission_lead_failed_evt";
			If (! isNil "_evt_idx") then {	_object removeEventHandler ["killed",_evt_idx]; };
			If (_destination_msg != "") then { ["d_msg",[_destination_msg,"all",objNull,"hq"]] call XNetCallEvent; };
			if (!isNil "_shared_alive") then {
				if (_shared_destination != "") then {[_shared_destination,true] call XNetSetJIP;};
			};
			if (_destination_code != "") then {
				["d_all_exec",[_destination_code,_object]] call XNetCallEvent;
			};
			_object setVariable ["mission_person_arrived",True,True];		
		};
	} else {
		["d_s_mexecute", ["XfMissionEventObjectiveLeadSuccess",_this]] call XNetCallEvent;
		_evt_idx = _object getVariable "mission_lead_failed_evt";
		If (! isNil "_evt_idx") then {	_object removeEventHandler ["killed",_evt_idx]; _object setVariable ["mission_lead_failed_evt", nil]; };
	};
};

XfMissionEventObjectiveArrestFailed = {
	if (isServer) then {
		// TODO: We can add a variable on the object to make sure we execute only once.
		private ["_object","_killer","_action","_parameters","_arrested_msg","_die_msg","_shared_arrested","_shared_alive","_arrested_code","_die_code","_destination","_evt_idx"];
		_object = _this select 0;
		_killer = _this select 1;
		_action = _object call XfMissionGetAction;
		_parameters = _object call XfMissionGetParameters;
		
		// Avoids we execute two times.
		_status = _object getVariable "mission_action_event_executed";
		_id = _object getVariable "mission_arrest_failed_evt";
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: obj:%4  id:%5  action:%6  status:%7  this:%8",diag_tickTime,"mission_functions.sqf","XfMissionEventObjectiveArrestFailed",typeof _object, _id, _action, _status, _this]};
		// if (!(isnil "_id") && !_status) then {
		if (_action == "ARREST" && !_status) then {
			_object setVariable ["mission_action_event_executed",true];

			_arrested_msg = _parameters select 0;
			_die_msg = _parameters select 1;
			_shared_arrested = _parameters select 2;
			_shared_alive = _parameters select 3;
			_arrested_code = "";
			if (count _parameters > 4) then {_arrested_code = _parameters select 4;};
			_die_code = "";
			if (count _parameters > 5) then {_die_code = _parameters select 5;};

			if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: obj:%4  amsg:%5  diemsg:%6  avar:%7  dvar:%8 acode:%9  diecode:%10",diag_tickTime,"mission_functions.sqf","XfMissionEventObjectiveArrestFailed",typeof _object, _arrested_msg,_die_msg, _shared_arrested, _shared_alive, (_arrested_code != ""), (_die_code != "")];};
			_evt_idx = _object getVariable "mission_arrest_failed_evt";
			If (! isNil "_evt_idx") then {	_object removeEventHandler ["killed",_evt_idx]; };

			If (_die_msg != "") then { ["d_msg",[_die_msg,"all",objNull,"hq"]] call XNetCallEvent; };
			if (!isNil "_shared_alive") then {
				if (_shared_alive != "") then {[_shared_alive,false] call XNetSetJIP;};
			};
			if (_die_code != "") then {
				["d_all_exec",[_die_code,_object]] call XNetCallEvent;
			};		
		};
	} else {
		["d_s_mexecute", ["XfMissionEventObjectiveArrestFailed",_this]] call XNetCallEvent;
		_evt_idx = _object getVariable "mission_arrest_failed_evt";
		If (! isNil "_evt_idx") then {	_object removeEventHandler ["killed",_evt_idx]; _object setVariable ["mission_arrest_failed_evt", nil]; };
	};
};

// This is executed when the person reaches the destination
XfMissionEventObjectiveArrestSuccess = {
	If (isServer) then {
		private ["_object","_action","_parameters","_arrested_msg","_die_msg","_shared_arrested","_shared_alive","_arrested_code","_die_code","_destination","_evt_idx"];
		_object = _this;
		_action = _object call XfMissionGetAction;
		_parameters = _object call XfMissionGetParameters;
		
		// Avoids we execute two times.
		_status = _object getVariable "mission_action_event_executed";
		_id = _object getVariable "mission_arrest_failed_evt";
		if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: obj:%4  id:%5  action:%6  status:%7  this:%8",diag_tickTime,"mission_functions.sqf","XfMissionEventObjectiveArrestFailed",typeof _object, _id, _action, _status, _this]};
		// if (!(isnil "_id") && !_status) then {
		if (_action == "ARREST" && !_status) then {
			_object setVariable ["mission_action_event_executed",true];
			
			_arrested_msg = _parameters select 0;
			_die_msg = _parameters select 1;
			_shared_arrested = _parameters select 2;
			_shared_alive = _parameters select 3;
			_arrested_code = "";
			if (count _parameters > 4) then {_arrested_code = _parameters select 4;};
			_die_code = "";
			if (count _parameters > 5) then {_die_code = _parameters select 5;};
			
			if (d_mission_debug_messages) then { diag_log Format["(%1)[%2]: %3: obj:%4  amsg:%5  diemsg:%6  avar:%7  dvar:%8  acode:%9  diecode:%10",diag_tickTime,"mission_functions.sqf","XfMissionEventObjectiveArrestSuccess",typeof _object, _arrested_msg,_die_msg, _shared_arrested, _shared_alive, (_arrested_code != ""), (_die_code != "")];};
			_evt_idx = _object getVariable "mission_arrest_failed_evt";
			If (! isNil "_evt_idx") then {	_object removeEventHandler ["killed",_evt_idx]; };

			doStop _object;
			[_object] joinsilent grpNull;
			doStop _object;
			_object addrating -1000;
			removeAllWeapons _object;
			_object setCaptive true;
			_object disableAI "AUTOTARGET";
			_object disableAI "MOVE";
			_object playMoveNow "AmovPercMstpSnonWnonDnon_AmovPercMstpSsurWnonDnon";

			If (_arrested_msg != "") then { ["d_msg",[_arrested_msg,"all",objNull,"hq"]] call XNetCallEvent; };
			if (!isNil "_shared_alive") then {
				if (_shared_arrested != "") then {[_shared_arrested,true] call XNetSetJIP;};
			};
			if (_arrested_code != "") then {
				["d_all_exec",[_arrested_code,_object]] call XNetCallEvent;
			};
		};
	} else {
		["d_s_mexecute", ["XfMissionEventObjectiveArrestSuccess",_this]] call XNetCallEvent;
		_evt_idx = _object getVariable "mission_arrest_failed_evt";
		If (! isNil "_evt_idx") then {	_object removeEventHandler ["killed",_evt_idx]; _object setVariable ["mission_arrest_failed_evt", nil]; };
	};
};

XfMissionEventObjectiveTakeOverKilled = {

};


XfMissionKamikaze = {
	private ["_side","_param","_kamikaze","_units","_distance","_ammo","_trust","_trigger","_destroy","_pos","_explo"];
	_side = _this select 0;
	if (typeName _side != "SIDE") exitWith {};
	_param = _this select 1;
	if (isNil "_param") exitWith {};
	if (typeName _param == "OBJECT") then {
		_kamikaze = _param;
	} else {
		if (typeName _param == "GROUP") then {
			_units = units _param;
			_kamikaze = _units select (floor (random (count _units)));
		};
	};
	if (typeName _kamikaze != "OBJECT") exitWith {};
	_distance = if (count _this > 2) then {_this select 2;} else {15;};
	if (typeName _distance != "SCALAR") exitWith {};
	_ammo = if (count _this > 3) then {_this select 3;} else {"R_57mm_HE";};
	if (typeName _ammo != "STRING") exitWith {};
	_trust = if (count _this > 4) then {_this select 4;} else {1;};
	if (typeName _trust != "SCALAR") exitWith {};
	_kamikaze allowFleeing 0;
	_kamikaze setskill ["courage",1];
	_kamikaze setskill ["spotDistance",0];
	_kamikaze setskill ["spotTime",0];
	_kamikaze disableAI "AUTOTARGET";
	_kamikaze disableAI "TARGET";
	_trigger = false;
	_destroy = false;
	_target = objNull;
	while {!_trigger && !(isNull _kamikaze)} do {
		_pos = position _kamikaze;
		_units = nearestObjects [_pos,["Man","Car","Tank","Motorcycle"],_distance];
		if (count _units > 0) then {
			{	if ((side _x == _side) && alive _x) exitWith {_trigger = true; _target = _x;};
				sleep 0.01;
			} forEach _units;
		};
		if ((damage (vehicle _kamikaze) > 0.7) || !(alive _kamikaze)) then {_trigger = true;_destroy = true;};
		if (_trigger) then {
			if (!isnull _target) then {
				_i_runs = 30;
				_kamikaze doMove (getPos _target);
				while { (_target distance _kamikaze) > 15 && _i_runs > 0 } do {
					_kamikaze doMove (getPos _target);
					sleep 0.5123;
				};
			};
			if (((random 1 <= _trust) && (_trust > 0)) || _destroy) then {
				_distance = (speed _kamikaze) / 3; 
				_pos = [_kamikaze,_distance,direction _kamikaze] call BIS_fnc_relPos;
				if (vehicle _kamikaze != _kamikaze) then {(vehicle _kamikaze) setDamage 1; };
				_explo = createVehicle [_ammo,_pos,[],0,"CAN_COLLIDE"];
				_explo setDamage 1;
				_kamikaze setDamage 1;
				if (!isNil "ACV_FNCT_Broadcast_BIS_Effects_Burn") then {[_pos,5,40] spawn ACV_FNCT_Broadcast_BIS_Effects_Burn;};
			} else {
				_pos = position _kamikaze;
				_fumi = createVehicle ["SmokeShell",_pos,[],0,"CAN_COLLIDE"];
				_fumi setDamage 1;
				_fumi attachTo [_kamikaze,[0,0,0.5]];
				sleep 3;
				deleteVehicle _fumi;
			};
		};
		sleep 1;
	};
};

d_mission_functions_compiled = True;